// ************************************************************************* // * (C)opyright 2026 by Ruben Carlo Benante * // * * // * This program is free software; you can redistribute it and/or modify * // * it under the terms of the GNU General Public License as published by * // * the Free Software Foundation, either version 3 of the License, or * // * (at your option) any later version. * // * * // * This program is distributed in the hope that it will be useful, * // * but WITHOUT ANY WARRANTY; without even the implied warranty of * // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * // * GNU General Public License for more details. * // * * // * You should have received a copy of the GNU General Public License * // * along with this program. If not, see http://www.gnu.org/licenses/. * // * * // * Contact author at: * // * Ruben Carlo Benante * // * rcb@beco.cc * // ************************************************************************* // The only module that touches the network. Uses Obsidian's requestUrl (not // fetch) so the HTTP Basic auth header and cross-origin call work. The pure // request/response helpers live in rpc.ts and are unit-tested there. import { requestUrl } from 'obsidian'; import { buildJsonRpcRequest, parseRpcResult, type RpcAuth } from './rpc'; // Perform a JSON-RPC call and return its `result`, throwing a readable Error // on transport failures or Kanboard-reported errors. async function call( auth: RpcAuth, method: string, params: Record, ): Promise { const req = buildJsonRpcRequest(auth, method, params); const resp = await requestUrl({ url: req.url, method: 'POST', headers: req.headers, body: req.body, throw: false, }); if (resp.status === 401) { throw new Error('Authentication failed (check username and API token)'); } let data: unknown; try { data = resp.json; } catch { throw new Error(`Unexpected response from Kanboard (HTTP ${resp.status})`); } return parseRpcResult(data); } // Resolve a board (Kanboard project) display name to its numeric id, or null. export async function getProjectIdByName( auth: RpcAuth, name: string, ): Promise { const result = await call(auth, 'getProjectByName', { name }); if (!result || typeof result !== 'object') return null; const id = (result as { id?: unknown }).id; return id ? Number(id) : null; } // Create a Kanboard task (which QualKard adopts as a new "new" card). Returns // the new task id. project_id + title are the only required fields; QualKard's // server-side hook fills in the spaced-repetition state on creation. export async function createTask( auth: RpcAuth, projectId: number, title: string, description: string, ): Promise { const result = await call(auth, 'createTask', { title, project_id: projectId, description, }); // createTask returns the new task id, or false on a validation failure. if (!result || typeof result === 'boolean') { throw new Error('Kanboard rejected the task (no id returned)'); } return Number(result); }