mirror of
https://github.com/TriliumNext/Notes.git
synced 2026-01-06 04:50:03 -06:00
ETAPI auth, spec improvements etc.
This commit is contained in:
@@ -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'),
|
||||
|
||||
128
src/public/app/dialogs/options/etapi.js
Normal file
128
src/public/app/dialogs/options/etapi.js
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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; });
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user