0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added initial support for the Admin API

refs https://github.com/TryGhost/Team/issues/1664

This allows us to have access to the currently logged in admin user as well as
future access to show and hide comments via the Admin API.
This commit is contained in:
Fabien "egg" O'Carroll 2022-07-05 15:21:04 +02:00
parent 19493073f6
commit ea531e8c19
2 changed files with 98 additions and 14 deletions

View file

@ -7,6 +7,28 @@ import AppContext from './AppContext';
import {hasMode} from './utils/check-mode'; import {hasMode} from './utils/check-mode';
import setupGhostApi from './utils/api'; import setupGhostApi from './utils/api';
import CommentsBox from './components/CommentsBox'; import CommentsBox from './components/CommentsBox';
import {useEffect} from 'react';
function AuthFrame({adminUrl, onLoad}) {
useEffect(function () {
onLoad();
}, []);
return (
<iframe data-frame="admin-auth" src={adminUrl + 'auth-frame'}></iframe>
);
}
function CommentsBoxContainer({done}) {
if (!done) {
return null;
}
return (
<ShadowRoot>
<CommentsBox />
</ShadowRoot>
);
}
function SentryErrorBoundary({dsn, children}) { function SentryErrorBoundary({dsn, children}) {
if (dsn) { if (dsn) {
@ -40,20 +62,21 @@ export default class App extends React.Component {
}; };
} }
componentDidMount() {
this.initSetup();
}
/** Initialize comments setup on load, fetch data and setup state*/ /** Initialize comments setup on load, fetch data and setup state*/
async initSetup() { async initSetup() {
try { try {
// Fetch data from API, links, preview, dev sources // Fetch data from API, links, preview, dev sources
const {site, member} = await this.fetchApiData(); const {site, member} = await this.fetchApiData();
const {comments, pagination} = await this.fetchComments(); const {comments, pagination} = await this.fetchComments();
this.adminApi = this.setupAdminAPI();
const admin = await this.adminApi.getUser();
/* eslint-disable no-console */
console.log(admin);
/* eslint-enable no-console */
const state = { const state = {
site, site,
member, member,
admin,
action: 'init:success', action: 'init:success',
initStatus: 'success', initStatus: 'success',
comments, comments,
@ -134,6 +157,69 @@ export default class App extends React.Component {
}; };
} }
setupAdminAPI() {
const frame = document.querySelector('iframe[data-frame="admin-auth"]');
let uid = 1;
let handlers = {};
window.addEventListener('message', function (event) {
if (event.origin !== '*') {
// return;
}
let data = null;
try {
data = JSON.parse(event.data);
} catch (err) {
/* eslint-disable no-console */
console.error('Error parsing event data', err);
/* eslint-enable no-console */
return;
}
const handler = handlers[data.uid];
if (!handler) {
return;
}
delete handlers[data.uid];
handler(data.error, data.result);
});
function callApi(action, args) {
return new Promise((resolve, reject) => {
function handler(error, result) {
if (error) {
return reject(error);
}
return resolve(result);
}
uid += 1;
handlers[uid] = handler;
frame.contentWindow.postMessage(JSON.stringify({
uid,
action,
...args
}), '*');
});
}
const api = {
async getUser() {
const result = await callApi('getUser');
return result.users[0];
},
hideComment(id) {
return callApi('hideComment', {id});
},
showComment(id) {
return callApi('showComment', {id});
}
};
return api;
}
/** Setup Sentry */ /** Setup Sentry */
setupSentry({site}) { setupSentry({site}) {
if (hasMode(['test'])) { if (hasMode(['test'])) {
@ -181,16 +267,13 @@ export default class App extends React.Component {
} }
render() { render() {
if (this.state.initStatus !== 'success') { const done = this.state.initStatus === 'success';
return null;
}
return ( return (
<SentryErrorBoundary dsn={this.props.sentryDsn}> <SentryErrorBoundary dsn={this.props.sentryDsn}>
<AppContext.Provider value={this.getContextFromState()}> <AppContext.Provider value={this.getContextFromState()}>
<ShadowRoot> <CommentsBoxContainer done={done}/>
<CommentsBox /> <AuthFrame adminUrl={this.props.adminUrl} onLoad={this.initSetup.bind(this)}/>
</ShadowRoot>
</AppContext.Provider> </AppContext.Provider>
</SentryErrorBoundary> </SentryErrorBoundary>
); );

View file

@ -31,9 +31,10 @@ function getSiteData() {
const siteUrl = scriptTag.dataset.ghostComments; const siteUrl = scriptTag.dataset.ghostComments;
const apiKey = scriptTag.dataset.key; const apiKey = scriptTag.dataset.key;
const apiUrl = scriptTag.dataset.api; const apiUrl = scriptTag.dataset.api;
const adminUrl = scriptTag.dataset.admin;
const sentryDsn = scriptTag.dataset.sentryDsn; const sentryDsn = scriptTag.dataset.sentryDsn;
const postId = scriptTag.dataset.postId; const postId = scriptTag.dataset.postId;
return {siteUrl, apiKey, apiUrl, sentryDsn, postId}; return {siteUrl, apiKey, apiUrl, sentryDsn, postId, adminUrl};
} }
return {}; return {};
} }
@ -53,12 +54,12 @@ function setup({siteUrl}) {
function init() { function init() {
// const customSiteUrl = getSiteUrl(); // const customSiteUrl = getSiteUrl();
const {siteUrl: customSiteUrl, sentryDsn, postId} = getSiteData(); const {siteUrl: customSiteUrl, sentryDsn, postId, adminUrl} = getSiteData();
const siteUrl = customSiteUrl || window.location.origin; const siteUrl = customSiteUrl || window.location.origin;
setup({siteUrl}); setup({siteUrl});
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
{<App siteUrl={siteUrl} customSiteUrl={customSiteUrl} sentryDsn={sentryDsn} postId={postId} />} {<App adminUrl={adminUrl} siteUrl={siteUrl} customSiteUrl={customSiteUrl} sentryDsn={sentryDsn} postId={postId} />}
</React.StrictMode>, </React.StrictMode>,
document.getElementById(ROOT_DIV_ID) document.getElementById(ROOT_DIV_ID)
); );