UNPKG

hackages

Version:

CLI tool for learning software development concepts through test-driven development

136 lines (135 loc) 4.66 kB
import open from "open"; import fs from "fs"; import path from "path"; import { printInfo, printSuccess, printError } from "../utils/console.js"; import { apiClient } from "./api.js"; import prompts from "prompts"; import { randomUUID } from "crypto"; function getConfigDir() { const homeDir = process.env.HOME || process.env.USERPROFILE || ""; const configDir = path.join(homeDir, ".hackages"); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } return configDir; } function getLearningGoalPath() { const learningPath = path.join(getConfigDir(), "learning.json"); if (!fs.existsSync(learningPath)) { fs.writeFileSync(learningPath, JSON.stringify({})); } return learningPath; } export function checkIfLearningJSONIsEmpty() { const learningGoalPath = getLearningGoalPath(); const learningGoal = JSON.parse(fs.readFileSync(learningGoalPath, "utf8")); return Object.keys(learningGoal).length === 0; } export function writeLearningGoal(learningGoal) { const learningJsonPath = getLearningGoalPath(); let learningData = {}; if (fs.existsSync(learningJsonPath)) { try { const fileContent = fs.readFileSync(learningJsonPath, "utf8"); learningData = JSON.parse(fileContent); } catch (err) { // If file is corrupted or unreadable, start fresh learningData = {}; } } // Add or update the new learning goal and set as current learningData.current = learningGoal.id; learningData[learningGoal.id] = learningGoal; fs.writeFileSync(learningJsonPath, JSON.stringify(learningData, null, 2)); } function getTokenPath() { return path.join(getConfigDir(), "auth.json"); } export function getStoredAuth() { try { const tokenPath = getTokenPath(); if (fs.existsSync(tokenPath)) { const data = fs.readFileSync(tokenPath, "utf8"); return JSON.parse(data); } } catch (error) { // If there's an error reading the token, treat as not authenticated } return null; } function storeAuth(auth) { try { const tokenPath = getTokenPath(); // first read the file then merge the auth object with the existing data const existingData = fs.existsSync(tokenPath) ? JSON.parse(fs.readFileSync(tokenPath, "utf8")) : {}; const mergedData = { ...existingData.user, ...auth.user }; fs.writeFileSync(tokenPath, JSON.stringify({ user: mergedData }, null, 2)); } catch (error) { printError(`Failed to store authentication: ${error}`); } } export function clearAuth() { try { const tokenPath = getTokenPath(); if (fs.existsSync(tokenPath)) { fs.unlinkSync(tokenPath); } } catch (error) { printError(`Failed to clear authentication: ${error}`); } } export async function loginWithGitHub() { try { // Open GitHub OAuth page const { authUrl } = await apiClient.auth.getAuthUrl(); printInfo("Opening GitHub login page..."); await open(authUrl); // Wait for user to complete OAuth flow printInfo("Please complete the GitHub login in your browser."); printInfo("After logging in, you'll be redirected to a page with a code."); printInfo("Copy that code and paste it here:"); // Get code from user input using prompts for better UX const { code } = await prompts({ type: "text", name: "code", message: "Enter the code from GitHub:", validate: (value) => (value.length > 0 ? true : "Please enter the code"), }); if (!code) { throw new Error("No code provided"); } // Exchange code for token const { authToken: token, user } = await apiClient.auth.login(code); // Store auth data storeAuth({ access_token: token, user }); printSuccess(`Successfully logged in as ${user?.name || user?.login}!`); } catch (error) { printError(`Login failed: ${error}`); throw error; } } export function isLoggedIn() { const data = getStoredAuth(); if (data && data.user && data.user.login) { return true; } return false; } export function getCurrentUser() { const auth = getStoredAuth(); if (auth && auth.user) { return auth.user; } // write to .hackages/auth.json {user: {login: "uuid"}} const user = { anonymousLogin: randomUUID(), createdAt: new Date().toISOString() }; storeAuth({ user }); return user; }