@ankimcp/anki-mcp-server
Version:
Model Context Protocol server for Anki - enables AI assistants to interact with your Anki flashcards
152 lines • 6.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CredentialsService = void 0;
const promises_1 = require("fs/promises");
const path_1 = require("path");
const os_1 = require("os");
const fs_1 = require("fs");
const common_1 = require("@nestjs/common");
class CredentialsService {
logger = new common_1.Logger(CredentialsService.name);
static CREDENTIALS_DIR = (0, path_1.join)((0, os_1.homedir)(), ".ankimcp");
static CREDENTIALS_FILE = (0, path_1.join)(CredentialsService.CREDENTIALS_DIR, "credentials.json");
getCredentialsPath() {
return CredentialsService.CREDENTIALS_FILE;
}
async saveCredentials(credentials) {
try {
await this.ensureCredentialsDirectory();
const content = JSON.stringify(credentials, null, 2);
await (0, promises_1.writeFile)(CredentialsService.CREDENTIALS_FILE, content, {
encoding: "utf-8",
mode: 0o600,
});
await (0, promises_1.chmod)(CredentialsService.CREDENTIALS_FILE, 0o600);
}
catch (error) {
throw new Error(`Failed to save credentials to ${CredentialsService.CREDENTIALS_FILE}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
}
}
async loadCredentials() {
try {
await (0, promises_1.access)(CredentialsService.CREDENTIALS_FILE, fs_1.constants.F_OK | fs_1.constants.R_OK);
const content = await (0, promises_1.readFile)(CredentialsService.CREDENTIALS_FILE, {
encoding: "utf-8",
});
const credentials = JSON.parse(content);
const validation = this.validateCredentials(credentials);
if (!validation.valid) {
this.logger.warn(`Corrupted credentials file at ${CredentialsService.CREDENTIALS_FILE}:`);
validation.errors.forEach((error) => {
this.logger.warn(` - ${error}`);
});
return null;
}
return credentials;
}
catch (error) {
if (error.code === "ENOENT") {
return null;
}
this.logger.warn(`Failed to load credentials from ${CredentialsService.CREDENTIALS_FILE}: ${error instanceof Error ? error.message : String(error)}`);
return null;
}
}
async clearCredentials() {
try {
await (0, promises_1.unlink)(CredentialsService.CREDENTIALS_FILE);
}
catch (error) {
if (error.code !== "ENOENT") {
throw new Error(`Failed to delete credentials file: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
}
}
}
async hasCredentials() {
try {
await (0, promises_1.access)(CredentialsService.CREDENTIALS_FILE, fs_1.constants.F_OK);
return true;
}
catch {
return false;
}
}
isTokenExpired(credentials) {
const expiresAt = new Date(credentials.expires_at).getTime();
const now = Date.now();
const bufferMs = 60 * 1000;
return now >= expiresAt - bufferMs;
}
async ensureCredentialsDirectory() {
try {
await (0, promises_1.access)(CredentialsService.CREDENTIALS_DIR, fs_1.constants.F_OK);
}
catch (error) {
if (error.code === "ENOENT") {
await (0, promises_1.mkdir)(CredentialsService.CREDENTIALS_DIR, {
recursive: true,
mode: 0o700,
});
}
else {
throw error;
}
}
}
validateCredentials(obj) {
const errors = [];
if (!obj || typeof obj !== "object") {
errors.push("Credentials must be an object");
return { valid: false, errors };
}
const creds = obj;
if (!("access_token" in creds)) {
errors.push("Missing field: access_token");
}
else if (typeof creds.access_token !== "string") {
errors.push(`Invalid type for access_token: expected string, got ${typeof creds.access_token}`);
}
if (!("refresh_token" in creds)) {
errors.push("Missing field: refresh_token");
}
else if (typeof creds.refresh_token !== "string") {
errors.push(`Invalid type for refresh_token: expected string, got ${typeof creds.refresh_token}`);
}
if (!("expires_at" in creds)) {
errors.push("Missing field: expires_at");
}
else if (typeof creds.expires_at !== "string") {
errors.push(`Invalid type for expires_at: expected string, got ${typeof creds.expires_at}`);
}
if (!("user" in creds)) {
errors.push("Missing field: user");
}
else if (!creds.user || typeof creds.user !== "object") {
errors.push(`Invalid type for user: expected object, got ${typeof creds.user}`);
}
else {
const user = creds.user;
if (!("id" in user)) {
errors.push("Missing field: user.id");
}
else if (typeof user.id !== "string") {
errors.push(`Invalid type for user.id: expected string, got ${typeof user.id}`);
}
if (!("email" in user)) {
errors.push("Missing field: user.email");
}
else if (typeof user.email !== "string") {
errors.push(`Invalid type for user.email: expected string, got ${typeof user.email}`);
}
if (!("tier" in user)) {
errors.push("Missing field: user.tier");
}
else if (user.tier !== "free" && user.tier !== "paid") {
errors.push(`Invalid tier value: expected 'free' or 'paid', got '${String(user.tier)}'`);
}
}
return { valid: errors.length === 0, errors };
}
}
exports.CredentialsService = CredentialsService;
//# sourceMappingURL=credentials.service.js.map