-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlogin.ts
More file actions
136 lines (117 loc) Β· 4.05 KB
/
login.ts
File metadata and controls
136 lines (117 loc) Β· 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { input, password } from "@inquirer/prompts";
import ora from "ora";
import { api, KeyflareApiError } from "../api/client.js";
import { writeConfig, writeApiKey, getApiUrl, readApiKey } from "../config.js";
import { log, error, bold, dim } from "../output/log.js";
import type { AuthVerifyResponse } from "@keyflare/shared";
export async function runLogin() {
log(bold("\nπ Keyflare Login\n"));
// Get current values
const currentUrl = getApiUrl();
const currentKey = readApiKey();
// Show current config status
const hasStoredUrl = !!currentUrl;
const hasStoredKey = !!currentKey;
if (hasStoredUrl || hasStoredKey) {
log(dim("Current config:"));
if (hasStoredUrl) log(dim(` API URL: ${currentUrl}`));
if (hasStoredKey) log(dim(` API Key: ${maskKey(currentKey!)}`));
log("");
log(dim("Press Enter to keep current value.\n"));
}
// Prompt for API URL - allow empty to keep current
const urlHint = hasStoredUrl ? ` (current: ${currentUrl})` : "";
const rawUrl = await input({
message: `Keyflare API URL${urlHint}`,
});
const apiUrl = rawUrl.trim() || (hasStoredUrl ? currentUrl : undefined);
if (!apiUrl) {
error("API URL is required (no current value to keep)");
process.exit(1);
}
// Validate URL
try {
new URL(apiUrl);
} catch {
error("Please enter a valid URL");
process.exit(1);
}
// Check if URL changed
const urlChanged = apiUrl !== currentUrl;
// Prompt for API key - allow empty to keep current
const keyHint = hasStoredKey ? ` (current: ${maskKey(currentKey!)})` : "";
const rawKey = await password({
message: `API Key${keyHint}`,
});
const apiKey = rawKey.trim() || (hasStoredKey ? currentKey : undefined);
if (!apiKey) {
error("API key is required (no current value to keep)");
process.exit(1);
}
// Check if key changed
const keyChanged = apiKey !== currentKey;
// If nothing changed, we're done
if (!urlChanged && !keyChanged) {
log(`\n${bold("β No changes")}\n`);
return;
}
// Save URL immediately so API client can use it
writeConfig({ apiUrl });
// If only URL changed (key unchanged), skip verification since key was already verified
if (!keyChanged) {
log(`\n${bold("β API URL updated!")}\n\n${dim("API URL:")} ${apiUrl}\n`);
return;
}
// Key changed - verify credentials
// Temporarily set the API key for verification
const originalEnvKey = process.env.KEYFLARE_API_KEY;
process.env.KEYFLARE_API_KEY = apiKey;
// Verify credentials by calling the API
const spinner = ora("Verifying credentials...").start();
let verifiedKeyType: "user" | "system" = "user";
try {
const verify = await api.get<AuthVerifyResponse>("/auth/verify");
verifiedKeyType = verify.key_type;
spinner.succeed("Credentials verified");
} catch (err: any) {
spinner.fail("Failed to verify credentials");
// Restore original state
if (originalEnvKey) {
process.env.KEYFLARE_API_KEY = originalEnvKey;
} else {
delete process.env.KEYFLARE_API_KEY;
}
if (err instanceof KeyflareApiError) {
if (err.status === 401) {
error("Invalid API key");
} else {
error(`API error: ${err.message}`);
}
} else {
error(err.message);
}
process.exit(1);
}
// Save the API key
writeApiKey(apiKey);
// Restore original env (the saved file will be used instead)
if (originalEnvKey) {
process.env.KEYFLARE_API_KEY = originalEnvKey;
} else {
delete process.env.KEYFLARE_API_KEY;
}
const keyTypeLabel = verifiedKeyType === "system" ? "system key" : "user key";
log(
`\n${bold("β Logged in!")}\n\n${dim("API URL:")} ${apiUrl}\n${dim("Key type:")} ${keyTypeLabel}\n${dim("Credentials saved to:")} ~/.config/keyflare/\n`
);
}
/**
* Mask a key for display, showing only prefix and length hint.
* E.g., "kfl_user_abc123..." β "kfl_user_abc*** (36 chars)"
*/
function maskKey(key: string): string {
if (!key) return "";
const prefix = key.slice(0, 12);
const rest = key.length > 12 ? "***" : "";
return `${prefix}${rest} (${key.length} chars)`;
}