ETAPI auth, spec improvements etc.

This commit is contained in:
zadam
2022-01-10 17:09:20 +01:00
parent 2d2641dbd7
commit 91dec23d5e
90 changed files with 1468 additions and 11753 deletions

View File

@@ -15,6 +15,7 @@ export async function showDialog(openTab) {
import('./options/shortcuts.js'),
import('./options/code_notes.js'),
import('./options/password.js'),
import('./options/etapi.js'),
import('./options/backup.js'),
import('./options/sync.js'),
import('./options/other.js'),

View File

@@ -0,0 +1,128 @@
import server from "../../services/server.js";
import utils from "../../services/utils.js";
const TPL = `
<h4>ETAPI</h4>
<p>ETAPI is a REST API used to access Trilium instance programmatically, without UI. <br/>
See more details on <a href="https://github.com/zadam/trilium/wiki/ETAPI">wiki</a> and <a onclick="window.open('etapi/etapi.openapi.yaml')" href="etapi/etapi.openapi.yaml">ETAPI OpenAPI spec</a>.</p>
<button type="button" class="btn btn-sm" id="create-etapi-token">Create new ETAPI token</button>
<br/><br/>
<h5>Existing tokens</h5>
<div id="no-tokens-yet">There are no tokens yet. Click on the button above to create one.</div>
<div style="overflow: auto; height: 500px;">
<table id="tokens-table" class="table table-stripped">
<thead>
<tr>
<th>Token name</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<style>
.token-table-button {
display: inline-block;
cursor: pointer;
padding: 3px;
margin-right: 20px;
font-size: large;
border: 1px solid transparent;
border-radius: 5px;
}
.token-table-button:hover {
border: 1px solid var(--main-border-color);
}
</style>
`;
export default class EtapiOptions {
constructor() {
$("#options-etapi").html(TPL);
$("#create-etapi-token").on("click", async () => {
const promptDialog = await import('../../dialogs/prompt.js');
const tokenName = await promptDialog.ask({
title: "New ETAPI token",
message: "Please enter new token's name",
defaultValue: "new token"
});
if (!tokenName.trim()) {
alert("Token name can't be empty");
return;
}
const {token} = await server.post('etapi-tokens', {tokenName});
await promptDialog.ask({
title: "ETAPI token created",
message: 'Copy the created token into clipboard. Trilium stores the token hashed and this is the last time you see it.',
defaultValue: token
});
this.refreshTokens();
});
this.refreshTokens();
}
async refreshTokens() {
const $noTokensYet = $("#no-tokens-yet");
const $tokensTable = $("#tokens-table");
const tokens = await server.get('etapi-tokens');
$noTokensYet.toggle(tokens.length === 0);
$tokensTable.toggle(tokens.length > 0);
const $tokensTableBody = $tokensTable.find("tbody");
$tokensTableBody.empty();
for (const token of tokens) {
$tokensTableBody.append(
$("<tr>")
.append($("<td>").text(token.name))
.append($("<td>").text(token.utcDateCreated))
.append($("<td>").append(
$('<span class="bx bx-pen token-table-button" title="Rename this token"></span>')
.on("click", () => this.renameToken(token.etapiTokenId, token.name)),
$('<span class="bx bx-trash token-table-button" title="Delete / deactive this token"></span>')
.on("click", () => this.deleteToken(token.etapiTokenId, token.name))
))
);
}
}
async renameToken(etapiTokenId, oldName) {
const promptDialog = await import('../../dialogs/prompt.js');
const tokenName = await promptDialog.ask({
title: "Rename token",
message: "Please enter new token's name",
defaultValue: oldName
});
await server.patch(`etapi-tokens/${etapiTokenId}`, {name: tokenName});
this.refreshTokens();
}
async deleteToken(etapiTokenId, name) {
if (!confirm(`Are you sure you want to delete ETAPI token "${name}"?`)) {
return;
}
await server.remove(`etapi-tokens/${etapiTokenId}`);
this.refreshTokens();
}
}

View File

@@ -11,9 +11,11 @@ const $form = $("#prompt-dialog-form");
let resolve;
let shownCb;
export function ask({ message, defaultValue, shown }) {
export function ask({ title, message, defaultValue, shown }) {
shownCb = shown;
$("#prompt-title").text(title || "Prompt");
$question = $("<label>")
.prop("for", "prompt-dialog-answer")
.text(message);
@@ -30,7 +32,7 @@ export function ask({ message, defaultValue, shown }) {
.append($question)
.append($answer));
utils.openDialog($dialog);
utils.openDialog($dialog, false);
return new Promise((res, rej) => { resolve = res; });
}

View File

@@ -118,7 +118,7 @@ class AppContext extends Component {
const appContext = new AppContext(window.glob.isMainWindow);
// we should save all outstanding changes before the page/app is closed
$(window).on('beforeunload', () => {
$(window).on('beforeunload', () => {return "SSS";
let allSaved = true;
appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter(wr => !!wr.deref());

View File

@@ -11,12 +11,12 @@ async function getInboxNote() {
/** @returns {NoteShort} */
async function getTodayNote() {
return await getDateNote(dayjs().format("YYYY-MM-DD"));
return await getDayNote(dayjs().format("YYYY-MM-DD"));
}
/** @returns {NoteShort} */
async function getDateNote(date) {
const note = await server.get('special-notes/date/' + date, "date-note");
async function getDayNote(date) {
const note = await server.get('special-notes/days/' + date, "date-note");
await ws.waitForMaxKnownEntityChangeId();
@@ -25,7 +25,7 @@ async function getDateNote(date) {
/** @returns {NoteShort} */
async function getWeekNote(date) {
const note = await server.get('special-notes/week/' + date, "date-note");
const note = await server.get('special-notes/weeks/' + date, "date-note");
await ws.waitForMaxKnownEntityChangeId();
@@ -34,7 +34,7 @@ async function getWeekNote(date) {
/** @returns {NoteShort} */
async function getMonthNote(month) {
const note = await server.get('special-notes/month/' + month, "date-note");
const note = await server.get('special-notes/months/' + month, "date-note");
await ws.waitForMaxKnownEntityChangeId();
@@ -43,7 +43,7 @@ async function getMonthNote(month) {
/** @returns {NoteShort} */
async function getYearNote(year) {
const note = await server.get('special-notes/year/' + year, "date-note");
const note = await server.get('special-notes/years/' + year, "date-note");
await ws.waitForMaxKnownEntityChangeId();
@@ -71,7 +71,7 @@ async function createSearchNote(opts = {}) {
export default {
getInboxNote,
getTodayNote,
getDateNote,
getDayNote,
getWeekNote,
getMonthNote,
getYearNote,

View File

@@ -36,6 +36,9 @@ async function processEntityChanges(entityChanges) {
loadResults.addOption(ec.entity.name);
}
else if (ec.entityName === 'etapi_tokens') {
// NOOP
}
else {
throw new Error(`Unknown entityName ${ec.entityName}`);
}

View File

@@ -389,16 +389,26 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.getTodayNote = dateNotesService.getTodayNote;
/**
* Returns date-note. If it doesn't exist, it is automatically created.
* Returns day note for a given date. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} date - e.g. "2019-04-29"
* @return {Promise<NoteShort>}
* @deprecated use getDayNote instead
*/
this.getDateNote = dateNotesService.getDayNote;
/**
* Returns day note for a given date. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} date - e.g. "2019-04-29"
* @return {Promise<NoteShort>}
*/
this.getDateNote = dateNotesService.getDateNote;
this.getDayNote = dateNotesService.getDayNote;
/**
* Returns date-note for the first date of the week of the given date. If it doesn't exist, it is automatically created.
* Returns day note for the first date of the week of the given date. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} date - e.g. "2019-04-29"

View File

@@ -41,6 +41,10 @@ async function put(url, data, componentId) {
return await call('PUT', url, data, {'trilium-component-id': componentId});
}
async function patch(url, data, componentId) {
return await call('PATCH', url, data, {'trilium-component-id': componentId});
}
async function remove(url, componentId) {
return await call('DELETE', url, null, {'trilium-component-id': componentId});
}
@@ -185,6 +189,7 @@ export default {
get,
post,
put,
patch,
remove,
ajax,
// don't remove, used from CKEditor image upload!

View File

@@ -245,10 +245,11 @@ function focusSavedElement() {
$lastFocusedElement = null;
}
async function openDialog($dialog) {
closeActiveDialog();
glob.activeDialog = $dialog;
async function openDialog($dialog, closeActDialog = true) {
if (closeActDialog) {
closeActiveDialog();
glob.activeDialog = $dialog;
}
saveFocusedElement();

View File

@@ -55,7 +55,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget {
this.$dropdownContent.on('click', '.calendar-date', async ev => {
const date = $(ev.target).closest('.calendar-date').attr('data-calendar-date');
const note = await dateNoteService.getDateNote(date);
const note = await dateNoteService.getDayNote(date);
if (note) {
appContext.tabManager.getActiveContext().setNote(note.noteId);

View File

@@ -219,6 +219,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
else if (command === "editTitle") {
const promptDialog = await import("../../dialogs/prompt.js");
const title = await promptDialog.ask({
title: "Rename note",
message: "Enter new note title:",
defaultValue: $title.text()
});