diff --git a/server/datastores/api.js b/server/datastores/api.js index 2568e41..727bd95 100755 --- a/server/datastores/api.js +++ b/server/datastores/api.js @@ -48,7 +48,7 @@ module.exports = class Datastore { * @param {string} newHash * @param {string} generateWasmName */ - putWasmInformationsToS3(pluginId, newHash, generateWasmName) { return Promise.resolve() } + putWasmInformationsToS3(email, pluginId, newHash, generateWasmName) { return Promise.resolve() } /** * Fetch wasm from datastore @@ -178,14 +178,13 @@ module.exports = class Datastore { * Check if received link is valid and add plugin to the current user * @param {string} pluginId */ - acceptInvitation = (userId, ownerId, pluginId) => Promise.resolve() + acceptInvitation = (userId, pluginId) => Promise.resolve() /** * Get invitation informations * @param {string} userId - * @param {string} ownerId * @param {string} pluginId * @returns */ - getInvitation = (userId, ownerId, pluginId) => Promise.resolve() + getInvitation = (userId, pluginId) => Promise.resolve() }; \ No newline at end of file diff --git a/server/datastores/s3.js b/server/datastores/s3.js index b2be1de..4d67458 100755 --- a/server/datastores/s3.js +++ b/server/datastores/s3.js @@ -119,10 +119,44 @@ module.exports = class S3Datastore extends Datastore { } } - getPlugin = async pluginId => { - const plugins = await this.getPlugins(pluginId); + hasRights = (email, pluginId) => { + return this.getPlugins() + .then(plugins => { - return plugins.find(plugin => plugin.pluginId === pluginId) + if (email === "*") + return + + const plugin = plugins.find(plugin => plugin.pluginId === pluginId) + + const users = plugin?.users || []; + const admins = plugin?.admins || []; + + if (users.includes(email) || admins.includes(email)) { + return plugin + } + + // TODO - better error handling + throw 'Not authorized' + }) + } + + getPlugin = async (email, pluginId) => { + const { instance, Bucket } = this.#state; + + await this.hasRights(email, pluginId) + + try { + const rawData = await instance.send(new GetObjectCommand({ + Bucket, + Key: `${pluginId}.json` + })) + const res = await new fetch.Response(rawData.Body) + .json() + + return res + } catch (err) { + return {} + } } putWasmFileToS3 = (wasmFolder) => { @@ -176,8 +210,8 @@ module.exports = class S3Datastore extends Datastore { } } - putWasmInformationsToS3 = (pluginId, newHash, generateWasmName) => { - return this.getPlugin(pluginId) + putWasmInformationsToS3 = (email, pluginId, newHash, generateWasmName) => { + return this.getPlugin(email, pluginId) .then(plugin => { let versions = plugin.versions || []; @@ -208,7 +242,7 @@ module.exports = class S3Datastore extends Datastore { versions } - return this.updatePlugin(pluginId, patchPlugin) + return this.updatePluginInformations(pluginId, patchPlugin) }) } @@ -384,8 +418,8 @@ module.exports = class S3Datastore extends Datastore { }) } - getConfigurations = pluginId => { - return this.getPlugin(pluginId) + getConfigurations = (email, pluginId) => { + return this.getPlugin(email, pluginId) .then(plugin => { const files = [{ ext: 'json', @@ -416,14 +450,15 @@ module.exports = class S3Datastore extends Datastore { .catch(err => { logger.error(err) }); } - deletePlugin = (pluginId) => { - return new Promise(resolve => this.getPlugin(pluginId) + deletePlugin = (email, pluginId) => { + return new Promise(resolve => this.getPlugin(email, pluginId) .then(plugin => { Promise.all([ this.deleteObject(`${pluginId}.zip`), this.deleteObject(`${pluginId}-logs.zip`), this.removeBinaries((plugin.versions || []).map(r => r.name)), - this.deleteObject(`${pluginId}.json`) + this.deleteObject(`${pluginId}.json`), + this.removePluginFromList(pluginId) ]) .then(() => resolve({ status: 204, body: null })) .catch(err => { @@ -435,14 +470,50 @@ module.exports = class S3Datastore extends Datastore { }) }) })) + .catch(_ => { + resolve({ + status: 400, + body: { + error: "something bad happened" + } + }) + }) } - updatePlugin = (id, body) => { + updatePluginInformations = (id, body) => { const { instance, Bucket } = this.#state; const params = { Bucket, Key: `${id}.json`, + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify(body)) + } + + console.log("updatePluginInformations", id, body) + + return instance.send(new PutObjectCommand(params)) + .then(() => ({ + status: 204, + body: null + })) + .catch(err => { + return { + status: err.$metadata.httpStatusCode, + body: { + error: err.Code, + status: err.$metadata.httpStatusCode + } + } + }) + } + + updatePlugin = (id, body) => { + const { instance, Bucket } = this.#state; + + const params = { + Bucket, + Key: `${id}.zip`, Body: body } @@ -467,6 +538,7 @@ module.exports = class S3Datastore extends Datastore { return new Promise(resolve => { const pluginId = crypto.randomUUID() + const newPlugin = isGithub ? { filename: metadata.repo, owner: metadata.owner, @@ -481,6 +553,8 @@ module.exports = class S3Datastore extends Datastore { template: metadata.template }; + console.log('generate pluginID', pluginId, newPlugin.pluginId) + const params = { Bucket, Key: `${pluginId}.json`, @@ -489,8 +563,9 @@ module.exports = class S3Datastore extends Datastore { } instance.send(new PutObjectCommand(params)) - .then(() => { - this.getPluginUsers + .then(async () => { + await this.addPluginToList(email, newPlugin); + const plugins = await this.getUserPlugins(email); resolve({ status: 201, body: { @@ -499,6 +574,7 @@ module.exports = class S3Datastore extends Datastore { }) }) .catch(err => { + console.log(err) resolve({ status: err.$metadata.httpStatusCode, body: { @@ -510,97 +586,149 @@ module.exports = class S3Datastore extends Datastore { }) } - patchPlugin = async (luginId, field, value) => { - const plugin = await getPlugin(pluginId) + patchPlugin = async (email, pluginId, field, value) => { + const plugin = await this.getPlugin(email, pluginId) - return this.updatePlugin(pluginId, { + return this.updatePluginInformations(pluginId, { ...plugin, [field]: value }) } - patchPluginName = (pluginId, newName) => { - return this.patchPlugin(pluginId, 'filename', newName) + patchPluginName = (email, pluginId, newName) => { + return this.patchPlugin(email, pluginId, 'filename', newName) } - patchPluginUsers = async (pluginId, newUsers, newAdmins) => { - const plugin = this.getPlugin(pluginId) + patchPluginUsers = async (email, pluginId, newUsers, newAdmins) => { + const plugins = await this.getPlugins() - return this.updatePlugin(pluginId, { - ...plugin, - users: newUsers, - admins: newAdmins - }) - } - - acceptInvitation = async (userEmail, ownerId, pluginId) => { - // try { - // const plugin = await this.getPlugin(pluginId) + const plugin = plugins.find(p => p.pluginId === pluginId) - // const newPlugin = { - // pluginId: ownerPlugin.pluginId, - // owner: ownerId - // } - - // return this.updatePlugin(pluginId, newPlugin) - // .then(() => true) - - // } catch (err) { - // console.log(err) - // return false - // } + if (plugin) { + this.updatePluginList(pluginId, { + ...plugin, + users: newUsers, + admins: newAdmins + }) + return { + status: 204, + data: null + } + } else { + return { + status: 404, + data: { + error: 'something bad happened' + } + } + } } getUserPlugins = async email => { const plugins = await this.getPlugins(); + console.log(plugins) - return plugins.find(plugin => { + const userPlugins = plugins.filter(plugin => { const users = plugin.users || []; const admins = plugin.admins || []; return users.includes(email) || admins.includes(email) - }) + }) || [] + + console.log("users plugins") + console.log(userPlugins, email) + + return Promise.all(userPlugins.map(plugin => this.getPlugin(email, plugin.pluginId))) + .then(plugins => { + return plugins.filter(data => Object.keys(data).length > 0) + }) } - getInvitation = async (userEmail, ownerId, pluginId) => { - // try { - // const ownerPlugins = await this.getUserPlugins(ownerId) + addPluginToList = async (email, plugin) => { + const { instance, Bucket } = this.#state; - // const ownerPlugin = ownerPlugins.find(plugin => plugin.pluginId === pluginId) + const plugins = await this.getPlugins() - // return ownerPlugin - // } catch (err) { - // console.log(err) - // return false - // } + return instance.send(new PutObjectCommand({ + Bucket, + Key: 'plugins.json', + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify([ + ...plugins, + { + pluginId: plugin.pluginId, + admins: [email], + users: [] + } + ])) + })) } - canSharePlugin = async (email, pluginId) => { - // const user = await this.getUser(email); + updatePluginList = async (pluginId, content) => { + const { instance, Bucket } = this.#state; - // const plugin = user.plugins.find(plugin => plugin.pluginId === pluginId); + const plugins = await this.getPlugins() - // if (plugin?.owner) { - // const owner = await this.getUser(plugin.owner) + return instance.send(new PutObjectCommand({ + Bucket, + Key: 'plugins.json', + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify(plugins.map(plugin => { + if (plugin.pluginId === pluginId) { + return content + } + return plugin + }))) + })) + } - // const rootPlugin = owner.plugins.find(plugin => plugin.pluginId === pluginId); + removePluginFromList = async pluginId => { + const { instance, Bucket } = this.#state; - // if (rootPlugin) { - // return (rootPlugin.admins || []).includes(email) - // } + const plugins = await this.getPlugins() - // return false - // } + return instance.send(new PutObjectCommand({ + Bucket, + Key: 'plugins.json', + ContentType: 'application/json', + Body: fromUtf8(JSON.stringify(plugins.filter(plugin => plugin.pluginId !== pluginId))) + })) + } + + getInvitation = (email, pluginId) => this.getPlugin(email, pluginId) - return true + canSharePlugin = async (email, pluginId) => { + const plugins = await this.getPlugins() + + const plugin = plugins.find(p => p.pluginId === pluginId) + + if (plugin) { + return plugin.admins.includes(email) + } + + return false } - getPluginUsers = async (pluginId) => { - const plugin = await this.getPlugin(pluginId) + getPluginUsers = async (email, pluginId) => { + const plugins = await this.getPlugins() + + const plugin = plugins.find(p => p.pluginId === pluginId) - return { - admins: plugin.admins, - users: plugin.users + if (plugin) { + return { + status: 200, + data: { + admins: plugin.admins, + users: plugin.users + } + } + } else { + return { + status: 404, + data: { + error: 'something bad happened' + } + } } } }; diff --git a/server/routers/invitation.js b/server/routers/invitation.js index 1780a78..b0afc07 100755 --- a/server/routers/invitation.js +++ b/server/routers/invitation.js @@ -4,43 +4,38 @@ const Datastore = require('../datastores'); const router = express.Router() router.get('/:id', (req, res) => { - const [userId, pluginId] = Buffer + const pluginId = Buffer .from(req.params.id, 'base64') .toString('ascii') - .split(":") - if (req.user.email !== userId) { - Datastore.getInvitation(req.user.email, userId, pluginId) - .then(body => { - if (body) { - return res.json(body) - } else { - return res.status(400).json({ error: 'something wrong happened' }) - } - }) - } else { - res.status(400).json({ error: 'something wrong happened' }) - } + Datastore.getInvitation(req.user.email, pluginId) + .then(body => { + if (body) { + return res.json(body) + } else { + return res.status(400).json({ error: 'something wrong happened' }) + } + }) + .catch(err => { + if (err === "Not authorized") { + res.redirect('/') + } + }) }) -router.post('/:hash', (req, res) => { - const [userId, pluginId] = Buffer - .from(req.params.hash, 'base64') - .toString('ascii') - .split(":") +// router.post('/:hash', (req, res) => { +// const pluginId = Buffer +// .from(req.params.id, 'base64') +// .toString('ascii') - if (req.user.email !== userId) { - Datastore.acceptInvitation(req.user.email, userId, pluginId) - .then(accepted => { - if (accepted) { - res.redirect(`/?pluginId=${pluginId}`) - } else { - res.redirect('/') - } - }) - } else { - res.redirect('/') - } -}) +// Datastore.acceptInvitation(req.user.email, pluginId) +// .then(accepted => { +// if (accepted) { +// res.redirect(`/?pluginId=${pluginId}`) +// } else { +// res.redirect('/') +// } +// }) +// }) module.exports = router diff --git a/server/routers/plugins.js b/server/routers/plugins.js index 8bba43e..75cc34f 100755 --- a/server/routers/plugins.js +++ b/server/routers/plugins.js @@ -148,28 +148,25 @@ router.put('/:id', (req, res) => { }) router.get('/:id/share-links', async (req, res) => { - const plugins = await Datastore.getUserPlugins(req.user.email) - - const owner = plugins.find(plugin => plugin.pluginId === req.params.id)?.owner || req.user.email - - const hash = Buffer.from(`${owner}:${req.params.id}`).toString('base64') + const hash = Buffer.from(req.params.id).toString('base64') res.json(`${ENV.SECURE_DOMAIN ? 'https' : 'http'}://${ENV.DOMAIN}:${ENV.EXPOSED_PORT || ENV.PORT}/invitation/${hash}`) }) router.put('/:id/users', (req, res) => { - Datastore.patchPluginUsers(req.params.id, + Datastore.patchPluginUsers(req.user.email, + req.params.id, req.body.users, - [...new Set([...req.body.admins, req.user.email])]) - .then(() => { + req.body.admins) + .then(data => { res - .status(204) - .json(null) + .status(data.status) + .json(data.body) }) }) router.get('/:id/users', (req, res) => { - Datastore.getPluginUsers(req.params.id) - .then(members => res.status(200).json(members)) + Datastore.getPluginUsers(req.user.email, req.params.id) + .then(out => res.status(out.status).json(out.data)) }) router.get('/:id/rights', (req, res) => { @@ -186,7 +183,7 @@ router.get('/:id/rights', (req, res) => { }) router.delete('/:id', async (req, res) => { - Datastore.deletePlugin(req.params.id) + Datastore.deletePlugin(req.user.email, req.params.id) .then(out => { res.status(out.status).json(out.body) }) @@ -342,15 +339,9 @@ router.post('/:id/build', async (req, res) => { const pluginId = req.params.id; const release = req.query.release === 'true'; - let user = req.user ? req.user.email : 'admin@otoroshi.io' - - const data = await Datastore.getUser(user) - let plugin = (data.plugins || []).find(p => p.pluginId === pluginId); + let user = req.user ? req.user.email : 'admin@otoroshi.io'; - if (plugin?.owner) { - user = plugin.owner; - plugin = await Datastore.getPlugin(plugin.owner, pluginId); - } + const plugin = await Datastore.getPlugin(user, pluginId); if (plugin.type === 'github') { plugin.type = req.query.plugin_type; diff --git a/server/routers/public.js b/server/routers/public.js index e7b9e6c..20079e9 100755 --- a/server/routers/public.js +++ b/server/routers/public.js @@ -67,27 +67,10 @@ router.get('/wasm/:id', (req, res) => { router.get('/plugins', (req, res) => { const reg = req.headers['kind'] || '*'; - if (reg === '*') { - Datastore.getUsers() - .then(r => { - const users = [...new Set([...(r || []), "adminotoroshiio"])]; - - if (users.length > 0) { - Promise.all(users.map(Datastore.getUser)) - .then(pluginsByUser => { - res.json(pluginsByUser - .map(user => user.plugins) - .flat() - .filter(f => f)) - }) - } else { - res.json([]) - } - }) - } else { - Datastore.getUser(reg) - .then(data => res.json(data.plugins)) - } + Datastore + .getPlugins() + .then(plugins => Promise.all(plugins.map(plugin => Datastore.getPlugin(reg, plugin.pluginId)))) + .then(plugins => res.json(plugins.filter(data => Object.keys(data).length > 0))) }); module.exports = router; \ No newline at end of file diff --git a/server/services/compiler/compiler.js b/server/services/compiler/compiler.js index a226084..d98e23e 100755 --- a/server/services/compiler/compiler.js +++ b/server/services/compiler/compiler.js @@ -155,7 +155,7 @@ class Compiler { .then(() => this.#websocketEmitMessage(buildOptions, "WASM has been saved ...")), Datastore.putBuildLogsToS3(`${buildOptions.plugin.id}-logs.zip`, buildOptions.logsFolder) .then(() => this.#websocketEmitMessage(buildOptions, "Logs has been saved ...")), - Datastore.putWasmInformationsToS3(buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) + Datastore.putWasmInformationsToS3(buildOptions.userEmail, buildOptions.plugin.id, buildOptions.plugin.hash, `${this.options.wasmName}.wasm`) .then(() => this.#websocketEmitMessage(buildOptions, "Informations has been updated")) ])) .then(() => { diff --git a/ui/src/App.js b/ui/src/App.js index 914d269..7a6b67f 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -14,7 +14,7 @@ class App extends React.Component { plugins: [], selectedPlugin: undefined, configFiles: [], - version: 'unknown' + version: '' } componentDidMount() { diff --git a/ui/src/InvitationHandler.js b/ui/src/InvitationHandler.js index eed498c..d020973 100644 --- a/ui/src/InvitationHandler.js +++ b/ui/src/InvitationHandler.js @@ -18,13 +18,21 @@ function InvitationHandler() { const paths = window.location.pathname.split("/invitation"); if (paths.length === 2) { - const invitationId = paths[1]; + const invitationId = paths[1].slice(1); Service.getInvitationInformation(invitationId) - .then(invit => setInvitation({ - ...invit, - invitationId - })) + .then(response => { + if (response.redirected) { + window.location.href = response.url; + } else { + response + .json() + .then(invit => setInvitation({ + ...invit, + invitationId + })) + } + }) } }, []) @@ -38,7 +46,7 @@ function InvitationHandler() { }}>
-

Invitation to edit {invitation.filename} plugin

+

You invited to edit {invitation.filename} plugin

{ - Service.acceptInvitation(invitation.invitationId) - .then(response => { - if (response.redirected) { - window.location.href = response.url; - } - }) + window.location.href = `/?pluginId=${atob(invitation.invitationId)}` + // Service.acceptInvitation(invitation.invitationId) + // .then(response => { + // if (response.redirected) { + // window.location.href = response.url; + // } + // }) }}> - Accept invitation and view plugin + View plugin
diff --git a/ui/src/services/index.js b/ui/src/services/index.js index c1d8324..2701dfa 100644 --- a/ui/src/services/index.js +++ b/ui/src/services/index.js @@ -134,7 +134,7 @@ export const getWasmRelease = wasmId => rawFetch(`/wasm/${wasmId}`); export const getAppVersion = () => f('/version'); -export const getInvitationInformation = invitationId => jsonFetch(`/invitations/${invitationId}`) +export const getInvitationInformation = invitationId => f(`/invitations/${invitationId}`) export const acceptInvitation = invitationId => f(`/invitations/${invitationId}`, { method: 'POST',