diff --git a/cli/README.md b/cli/README.md index 4c87587..64c200a 100644 --- a/cli/README.md +++ b/cli/README.md @@ -67,7 +67,10 @@ Then open the content of your `my-first-plugin` folder. You should find the gene You can now optionally start a new plugin from a template by appending `--template=[template-name]` to the creation command. -If you don't pass a template, Wasmo will list the available templates: `js`, `ts`, `opa`, `go` and `rust`. +If you don't pass a template, Wasmo will list the available templates. There are listed by product : + - empty template : `js`, `ts`, `opa`, `go` and `rust` + - Otoroshi template : `otoroshi_go`, `otoroshi_rust`, `otoroshi_opa`, `otoroshi_ts`, `otoroshi_js` + - Izanami template : `izanami_js`, `izanami_go`, `izanami_rust`, `izanami_opa`, `izanami_ts` ``` wasmo init --name=my-first-plugin --template=[template-name] --path=[output-directory] diff --git a/docs/documentation/app/builder/collaborate/_page.mdx b/docs/documentation/app/builder/collaborate/_page.mdx index 048658a..d994964 100644 --- a/docs/documentation/app/builder/collaborate/_page.mdx +++ b/docs/documentation/app/builder/collaborate/_page.mdx @@ -3,10 +3,10 @@ Wasmo 1.2.2 brings a new feature to collaborate inside the product. You can work together on a plugin by sharing it with collaborators. It's easy to share plugins with your entire team. -1. In Wasmo, on the left, click Share plugin. -2. Email can be added to two lists : the first to allow users to edit, view and share plugin, and the second, more restrictive, where users can only -edit and view plugins. -3. Click Update authorized people +To share plugins from the UI: + 1. Click on the desired plugin + 2. Click the 🔗 button + 3. Write administrators and users emails in their respective lists
diff --git a/docs/documentation/app/cli/getting-started/_page.mdx b/docs/documentation/app/cli/getting-started/_page.mdx index 643213a..6851926 100644 --- a/docs/documentation/app/cli/getting-started/_page.mdx +++ b/docs/documentation/app/cli/getting-started/_page.mdx @@ -43,7 +43,10 @@ Then open the content of your `my-first-plugin` folder. You should find the gene You can now optionally start a new plugin from a template by appending `--template=[template-name]` to the creation command. -If you don't pass a template, Wasmo will list the available templates: `js`, `ts`, `opa`, `go` and `rust`. +If you don't pass a template, Wasmo will list the available templates. There are listed by product : + - empty template : `js`, `ts`, `opa`, `go` and `rust` + - Otoroshi template : `otoroshi_go`, `otoroshi_rust`, `otoroshi_opa`, `otoroshi_ts`, `otoroshi_js` + - Izanami template : `izanami_js`, `izanami_go`, `izanami_rust`, `izanami_opa`, `izanami_ts` ``` wasmo init --name=my-first-plugin --template=[template-name] --path=[output-directory] @@ -61,7 +64,11 @@ You have two ways to build your plugin: [wasmoserver]: https://github.com/MAIF/wasmo -Assuming we want to build our `my-first-plugin` locally. Enter `wasmo build --host=OneShotDocker --path=my-first-plugin` to start the build. +Assuming we want to build our `my-first-plugin` locally. Enter the following command to start the build. + +``` +wasmo build --host=OneShotDocker --path=my-first-plugin +``` Let's explain these 3 parameters: - the `path` parameter is explicitly used to indicate the plugin to build diff --git a/docs/documentation/app/faq/_page.mdx b/docs/documentation/app/faq/_page.mdx new file mode 100644 index 0000000..5160f5c --- /dev/null +++ b/docs/documentation/app/faq/_page.mdx @@ -0,0 +1,109 @@ +import Badge from '../../components/Badge' +import Badges from '../../components/Badges' +import FAQButton from '../../components/FAQButton' + +# FAQ + + + + + + + + + + +### What is the fastest way to use Wasmo? + +``` +$ cargo install wasmo +or +$ brew tap maif/wasmo +$ brew install wasmo +``` + +### How to create an Otoroshi-compatible plugin using Docker? + + +Initialize the plugin with corresponding Otoroshi template and Javascript language +``` +wasmo init --template=otoroshi_js --name=foo +``` + +Build plugin from folder and Docker +``` +wasmo build --host=OneShotDocker --path=. +``` + +### How can I create a new development version of my plugin? + + +Rust plugin + +``` Cargo.toml +[package] +name = "foo" +version = "1.0.2" + +... +``` + +JS/TS/Open Policy Agent plugin + +``` package.json +{ + "name": "foo", + "version": "1.0.2", + ... +} +``` + +Go plugin + +``` go.mod +module foo/1.0.2 + +... +``` + +
+ + +
+ ### Can I download the generated Wasm from the UI? + + Once you have built a dev or release version of your plugin using the Hammer or Rocker buttons (available at the top right of the screen), + you can click on each version under the 'Releases' section on the left side of the screen. + + ### Can I determine who built each version and at what time? + + Each plugin has a **config** file under the **configuration** section with the following information : + - type: language used to develop the plugin. + - users: list of users allowed to edit and view the plugin. + - admins: list of admins allowed to edit, view and share the plugin. + - filename: name of the plugin. + - pluginId: unique ID of the plugin. + - template: original template, selected at plugin creation. + - **versions: list of built versions, with name, creator and date of generation**. + - last_hash: hash used by the backend to check if changes has been made between last version. +
+
+ +### How can I collaborate with my team? + +Since version 1.22, Wasmo allows users to share plugins with two levels of rights: +- `users`: Can edit and view plugin. +- `admins`: Can edit, view and share plugin. + +You can find more information about sharing by reading this [article](/wasmo/builder/collaborate) + +### Our team have a CI/CD process and wants to automate the building of our plugins. + +Since version 1.x, Wasmo includes a command line interface to create, edit and build plugins. + +You can find more [information](/wasmo/cli/getting-started) about the CLI and the Github [repository](https://github.com/MAIF/wasmo/tree/main/cli) \ No newline at end of file diff --git a/docs/documentation/app/faq/layout.js b/docs/documentation/app/faq/layout.js new file mode 100644 index 0000000..4962903 --- /dev/null +++ b/docs/documentation/app/faq/layout.js @@ -0,0 +1,7 @@ +import Page from './page'; + +export const metadata = { + title: 'FAQ', +} + +export default Page; \ No newline at end of file diff --git a/docs/documentation/app/faq/page.js b/docs/documentation/app/faq/page.js new file mode 100644 index 0000000..2ec87a6 --- /dev/null +++ b/docs/documentation/app/faq/page.js @@ -0,0 +1,20 @@ +"use client" + +import Layout from '@/components/Layout'; +import Page from './_page.mdx'; + +export default function Home() { + + return + + + +} \ No newline at end of file diff --git a/docs/documentation/app/globals.css b/docs/documentation/app/globals.css index a8aa6a2..6e2ff3e 100644 --- a/docs/documentation/app/globals.css +++ b/docs/documentation/app/globals.css @@ -16,4 +16,38 @@ overflow: auto; max-height: calc(500px - 71px); margin-bottom: 24px; +} + +.sidebar-group::before { + position: absolute; + bottom: -0.1rem; + top: -0.1rem; + left: -0.5rem; + width: 10px; + --tw-border-opacity: 1; + border-right-width: 2px; + border-color: rgb(226 232 240 / var(--tw-border-opacity)); + opacity: 1; + content: ""; +} + +.sidebar-group-selected::before { + border-color: rgb(126 34 206); +} + +h2, h3 { + position: relative; +} + +.anchor-link { + color: #666; + opacity: 0; + position: absolute; + transform: translate(-1em, -2px); + width: 1em; +} + +h3:hover .anchor-link, +h2:hover .anchor-link { + opacity: 1; } \ No newline at end of file diff --git a/docs/documentation/components/Badge.js b/docs/documentation/components/Badge.js new file mode 100644 index 0000000..c8eec99 --- /dev/null +++ b/docs/documentation/components/Badge.js @@ -0,0 +1,7 @@ +export default function Badge({ value, raw, ...props }) { + return
+ + {!raw ? `<${value}>` : value} + +
+} \ No newline at end of file diff --git a/docs/documentation/components/Badges.js b/docs/documentation/components/Badges.js index 88e92f5..23682fa 100644 --- a/docs/documentation/components/Badges.js +++ b/docs/documentation/components/Badges.js @@ -1,5 +1,5 @@ -export default function Badges({ values, raw }) { - return
+export default function Badges({ values, raw, ...props }) { + return
{values.map(value => {!raw ? `<${value}>` : value} )} diff --git a/docs/documentation/components/FAQButton.js b/docs/documentation/components/FAQButton.js new file mode 100644 index 0000000..eb64260 --- /dev/null +++ b/docs/documentation/components/FAQButton.js @@ -0,0 +1,21 @@ +export default function FAQButton({ title }) { + return +} \ No newline at end of file diff --git a/docs/documentation/components/Layout.js b/docs/documentation/components/Layout.js index 080cd37..1e9cd54 100644 --- a/docs/documentation/components/Layout.js +++ b/docs/documentation/components/Layout.js @@ -49,7 +49,7 @@ function Layout({ children, next, metadata, previous }) { table: Table, th: props => {props.children}, thead: props => {props.children}, - h3: props =>

{props.children}

, + h3: Heading.H3, h4: props =>

{props.children}

}}> {children} diff --git a/docs/documentation/components/Searchbar.js b/docs/documentation/components/Searchbar.js index 53f47b2..ab93494 100644 --- a/docs/documentation/components/Searchbar.js +++ b/docs/documentation/components/Searchbar.js @@ -70,7 +70,9 @@ export default function Searchbar({ handleOpen, open }) { paddingTop: open ? 20 : 6 }}> - {!open &&
+ {!open &&
  • Overview
  • +
  • + + FAQ +
    New
    +
    +
  • {Object.entries(LINKS).map(([group, children]) => { return
  • @@ -65,15 +95,38 @@ export function Sidebar({ metadata }) { -
      +
        {children.map(child => { const href = `/wasmo/${slugify(group)}/${slugify(child)}`; - return
      • + return
      • {child} + + {NEWS.includes(child) &&
        New
        }
      • })} diff --git a/docs/documentation/components/mdx/Heading.js b/docs/documentation/components/mdx/Heading.js index 26384bd..5d2741b 100644 --- a/docs/documentation/components/mdx/Heading.js +++ b/docs/documentation/components/mdx/Heading.js @@ -1,6 +1,38 @@ +function getAnchor(children) { + const text = Array.isArray(children) ? children[0] : children + return ("" + text) + .toLowerCase() + .replace(/[^a-z0-9 ]/g, '') + .replace(/[ ]/g, '-'); +} + export const Heading = { H1: ({ children }) =>

        {children}

        , - H2: props => { - return

        {props.children}

        + // H2: props => { + // return

        {props.children}

        + // }, + H2: ({ children }) => { + const anchor = getAnchor(children); + const link = `#${anchor}`; + return ( +

        + + § + + {children} +

        + ); }, + H3: ({ children }) => { + const anchor = getAnchor(children); + const link = `#${anchor}`; + return ( +

        + + § + + {children} +

        + ); + } }; \ No newline at end of file diff --git a/docs/documentation/components/mdx/Pre.js b/docs/documentation/components/mdx/Pre.js index 96f6cde..ec73f26 100644 --- a/docs/documentation/components/mdx/Pre.js +++ b/docs/documentation/components/mdx/Pre.js @@ -31,15 +31,16 @@ export const Pre = props => { {!['javascript', 'go', 'rust', 'js'].includes(language) && {language}}
  • } -
    { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(codeString); setCopied(true) } - }} - style={{ - border: '1px solid #fff' }}> diff --git a/docs/documentation/public/release.png b/docs/documentation/public/release.png new file mode 100644 index 0000000..36f2f70 Binary files /dev/null and b/docs/documentation/public/release.png differ diff --git a/server/datastores/postgres.js b/server/datastores/postgres.js index 8e5ee7c..50f3250 100755 --- a/server/datastores/postgres.js +++ b/server/datastores/postgres.js @@ -10,10 +10,6 @@ const { Pool } = require('pg'); const logger = require("../logger"); const { isAString } = require('../utils'); -/** - * Class representing PG. - * @extends Datastore - */ module.exports = class PgDatastore extends Datastore { /** @type {S3Datastore} */ #sourcesDatastore = undefined; @@ -159,25 +155,18 @@ module.exports = class PgDatastore extends Datastore { } hasRights = (email, pluginId) => { + if (email === "*") + return Promise.resolve() + return this.#pool.connect() - .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text", [pluginId]) + .then(client => client.query("SELECT * FROM plugins WHERE id = $1::text AND (content->'admins' ? $2::text OR content->'users' ? $2::text)", [pluginId, email]) .then(res => { client.release() - return res.rowCount === 1 ? res.rows[0].content : {} + return res.rowCount === 1 })) - .then(plugin => { - if (email === "*") - return - - const users = plugin?.users || []; - const admins = plugin?.admins || []; - - if (users.includes(email) || admins.includes(email)) { - return plugin - } - - // TODO - better error handling - throw 'Not authorized' + .then(authorized => { + if (!authorized) + throw 'Not authorized' }) } diff --git a/server/datastores/s3.js b/server/datastores/s3.js index 69aee8c..29dfc09 100755 --- a/server/datastores/s3.js +++ b/server/datastores/s3.js @@ -5,8 +5,7 @@ const { GetObjectCommand, S3Client, HeadBucketCommand, CreateBucketCommand, - DeleteObjectCommand, - DeleteBucketCommand + DeleteObjectCommand } = require("@aws-sdk/client-s3"); const { fromUtf8 } = require("@aws-sdk/util-utf8-node"); @@ -23,10 +22,6 @@ const AdmZip = require("adm-zip"); const { Console } = require('console'); const CustomStream = require('./CustomStream'); -/** - * Class representing S3. - * @extends Datastore - */ module.exports = class S3Datastore extends Datastore { #state = { instance: undefined, diff --git a/ui/src/TabsHeader.js b/ui/src/TabsHeader.js index 98cce4a..a7abcf9 100644 --- a/ui/src/TabsHeader.js +++ b/ui/src/TabsHeader.js @@ -6,7 +6,7 @@ import { toast } from 'react-toastify' import Select from 'react-select/creatable'; export function TabsHeader({ - selectedPlugin, onSave, onBuild, onDownload, + selectedPlugin, onSave, onBuild, showPlaySettings, children }) { if (!selectedPlugin?.pluginId) @@ -16,20 +16,15 @@ export function TabsHeader({ selectedPluginType={selectedPlugin?.type} onSave={onSave} onBuild={onBuild} - onDownload={onDownload} showActions={!!selectedPlugin} showPlaySettings={showPlaySettings} pluginId={selectedPlugin?.pluginId} - // users={selectedPlugin?.users} - // admins={selectedPlugin?.admins} > {children} } -function Header({ - children, onSave, onBuild, showActions, onDownload, - showPlaySettings, pluginId }) { +function Header({ children, onSave, onBuild, showActions, showPlaySettings, pluginId }) { const [runtimeState, setRuntimeEnvironment] = useState(false); const [canShare, setCanSharePlugin] = useState(false); @@ -73,7 +68,6 @@ function Header({ - {runtimeState && } }
    @@ -213,9 +207,9 @@ function ShareButton(props) { @@ -248,16 +242,7 @@ function Release({ onBuild }) { tooltip="Release" className="navbar-item" onClick={() => onBuild(true)}> - - -} - -function Download({ onDownload }) { - return } diff --git a/ui/src/TabsManager.js b/ui/src/TabsManager.js index 615287d..a04285c 100644 --- a/ui/src/TabsManager.js +++ b/ui/src/TabsManager.js @@ -70,6 +70,7 @@ function TabsManager({ plugins, ...props }) { onNewPlugin={props.onNewPlugin} setFilename={props.onPluginNameChange} removePlugin={props.removePlugin} + onDownload={props.onDownload} enablePluginRenaming={props.enablePluginRenaming} /> {props.selectedPlugin && } - {props.selectedPlugin && } + {props.selectedPlugin &&
    + + +
    } } diff --git a/ui/src/index.css b/ui/src/index.css index a706e20..946b09d 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -73,6 +73,7 @@ h1 { [tooltip]::before, [tooltip]::after { text-transform: none; + right: -50%; font-size: 0.75em; line-height: 1; user-select: none;