-
Notifications
You must be signed in to change notification settings - Fork 48
DOCS-3472: Add documentation for Viam Apps #4335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a6bb349
aa38d51
14fad22
660da6e
6b62ff7
79b1fc4
c12df67
9c78c33
cc31474
aedca96
d587cff
939bd61
503826a
3102de7
36f207b
c0bb95f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
--- | ||
title: "Create a Viam app" | ||
linkTitle: "Create a Viam app" | ||
weight: 5 | ||
layout: "docs" | ||
type: "docs" | ||
description: "Create and deploy a custom web interface for your machines without managing hosting and authentication." | ||
--- | ||
|
||
Create and deploy a custom web interface for your machines without managing hosting and authentication. | ||
Once deployed, your app is accessible from a dedicated URL (`appname_publicnamespace.viamapplications.com`), and hosting and authentication is handled for you. | ||
|
||
Users log into your app and select a machine they have access to. | ||
The app then renders your custom interface for interacting with the user's machine. | ||
|
||
{{<gif webm_src="/spa.webm" mp4_src="/spa.mp4" alt="Sample web app" max-width="500px">}} | ||
|
||
## Requirements | ||
|
||
{{< expand "Install the Viam CLI and authenticate." >}} | ||
Install the Viam CLI using the option below that matches your system architecture: | ||
|
||
{{< readfile "/static/include/how-to/install-cli.md" >}} | ||
|
||
Then authenticate your CLI session with Viam using one of the following options: | ||
|
||
{{< readfile "/static/include/how-to/auth-cli.md" >}} | ||
|
||
{{< /expand >}} | ||
|
||
## Build a custom web interface | ||
|
||
You can build a custom web interface to access your machines using your preferred framework like React, Vue, Angular, or others. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment below about this heading. Along those lines, I think this section should link to examples of these, and basically say "go off and develop an app then come back" |
||
|
||
### Access machines from your app | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But, shouldn't users just be able to log in? It's unclear who needs to do this and when and why |
||
|
||
When logging into a Viam app and selecting a machine to use it with, the machine's API key is stored as a cookie. | ||
You can access the data from your browser's cookies as follows: | ||
|
||
```ts {class="line-numbers linkable-line-numbers" data-line=""} | ||
import Cookies from "js-cookie"; | ||
|
||
let apiKeyId = ""; | ||
let apiKeySecret = ""; | ||
let hostname = ""; | ||
let machineId = ""; | ||
|
||
machineCookie = window.location.pathname.split("/")[2]; | ||
({ | ||
id: apiKeyId, | ||
key: apiKeySecret, | ||
hostname: hostname, | ||
machineId: machineId | ||
} = JSON.parse(Cookies.get(machineCookie)!)); | ||
|
||
For developing your app on localhost, add the same information to your browser's cookies: | ||
|
||
1. Navigate to [Camera Viewer](https://camera-viewer_naomi.viamapplications.com/). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add explanation of why we are going to your demo app? If I'm making my own app that has nothing to do with cameras this feels like a strange step There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that'll go away once the cookie issue is fixed so will leave for not |
||
2. Log in and select the machine you'd like to use for testing. | ||
3. Open Developer Tools and go to the console. | ||
4. Execute the following JavaScript to obtain the cookies you need: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [opt] Maybe tell users how (i.e., "Type "allow pasting" into the console, then paste the following script....") |
||
|
||
```js {class="line-numbers linkable-line-numbers" data-line=""} | ||
function generateCookieSetterScript() { | ||
// Get all cookies from current page | ||
const currentCookies = document.cookie.split(";"); | ||
let cookieSetterCode = "// Cookie setter script for localhost\n"; | ||
cookieSetterCode += | ||
"// Copy and paste this entire script into your browser console when on localhost\n\n"; | ||
|
||
// Process each cookie | ||
let cookieCount = 0; | ||
currentCookies.forEach((cookie) => { | ||
if (cookie.trim()) { | ||
// Extract name and value from the cookie | ||
const [name, value] = cookie.trim().split("="); | ||
|
||
// Add code to set this cookie | ||
cookieSetterCode += `document.cookie = "${name}=${value}; path=/";\n`; | ||
cookieCount++; | ||
} | ||
}); | ||
|
||
// Add summary comment | ||
cookieSetterCode += `\nconsole.log("Set ${cookieCount} cookies on localhost");\n`; | ||
|
||
// Display the generated code | ||
console.log(cookieSetterCode); | ||
|
||
// Create a textarea element to make copying easier | ||
const textarea = document.createElement("textarea"); | ||
textarea.value = cookieSetterCode; | ||
textarea.style.position = "fixed"; | ||
textarea.style.top = "0"; | ||
textarea.style.left = "0"; | ||
textarea.style.width = "100%"; | ||
textarea.style.height = "250px"; | ||
textarea.style.zIndex = "9999"; | ||
document.body.appendChild(textarea); | ||
textarea.focus(); | ||
textarea.select(); | ||
} | ||
|
||
// Execute the function | ||
generateCookieSetterScript(); | ||
``` | ||
|
||
5. Copy the resulting script. It will look like this: | ||
|
||
```js {class="line-numbers linkable-line-numbers" data-line=""} | ||
// Cookie setter script for localhost | ||
// Copy and paste this entire script into your browser console when on localhost | ||
|
||
document.cookie = "<SECRET COOKIE INFO>; path=/"; | ||
document.cookie = "machinesWhoseCredentialsAreStored=<MACHINE ID>; path=/"; | ||
|
||
console.log("Set 2 cookies on localhost"); | ||
``` | ||
|
||
6. Open the app you are building on localhost and run the resulting script. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I haven't started building an app yet have I? If I should have, the top of the doc should be tweaked to indicate this. |
||
7. Reload your app. | ||
|
||
### Configure routing | ||
|
||
When using your deployed application, static files will be accessible at `https://your-app-name_your-public-namespace.viamapplications.com/machine/<machine-id>/`. | ||
npentrel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
If your HTML file loads other files, use relative paths to ensure your files are accessible. | ||
|
||
## Deploy your web interface as a Viam app | ||
|
||
To deploy your app with Viam you must package it as a module and upload it using the Viam CLI. | ||
|
||
{{< table >}} | ||
{{% tablestep number=1 %}} | ||
|
||
**Create a <FILE>meta.json</FILE>** file for your module using this template: | ||
|
||
{{< tabs >}} | ||
{{% tab name="Template" %}} | ||
|
||
```json | ||
{ | ||
"module_id": "your-namespace:your-module", | ||
"visibility": "public", | ||
"url": "https://github.com/your-org/your-repo", | ||
"description": "Your module description", | ||
"applications": [ | ||
{ | ||
"name": "your-app-name", | ||
"type": "single_machine", | ||
"entrypoint": "dist/index.html" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
{{% /tab %}} | ||
{{% tab name="Example" %}} | ||
|
||
```json | ||
{ | ||
"module_id": "acme:dashboard", | ||
"visibility": "public", | ||
"url": "https://github.com/acme/dashboard", | ||
"description": "An example dashboard for a fictitious company called Acme.", | ||
"applications": [ | ||
{ | ||
"name": "dashboard", | ||
"type": "single_machine", | ||
"entrypoint": "dist/index.html" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
{{% /tab %}} | ||
{{< /tabs >}} | ||
|
||
This file specifies the contents of the module. | ||
It is required for your module. | ||
|
||
{{% expand "Click to view more information on attributes." %}} | ||
|
||
<!-- prettier-ignore --> | ||
| Name | Type | Inclusion | Description | | ||
|------|------|-----------|-------------| | ||
| `module_id` | string | **Required** | The module ID, which includes the organization name and the module name. `module_id` uniquely identifies your module. | | ||
| `visibility` | string | **Required** | Must be `"public"`. | | ||
| `description` | string | **Required** | A description of your module and what it provides. | | ||
| `url` | string | Optional | The URL of the GitHub repository containing the source code of the module. | | ||
| `applications` | array | Optional | Objects that provide information about the [apps](/operate/control/viam-app/) associated with the module. | | ||
| `models` | array | Optional | Empty unless you are shipping the app alongside models. For information on how to add models, see [Integrate other hardware](/operate/get-started/other-hardware/). | | ||
|
||
{{% /expand%}} | ||
|
||
The `applications` field is an array of application objects with the following properties: | ||
|
||
<!-- prettier-ignore --> | ||
| Property | Type | Description | | ||
| ------------ | ------ | ----------- | | ||
| `name` | string | The name of your application, which will be a part of the app's URL (`name_publicnamespace.viamapplications.com`). For more information on valid names see [Valid application identifiers](/operate/reference/naming-modules/#valid-application-identifiers). | | ||
| `type` | string | The type of application (currently only `"single_machine"` is supported). | | ||
| `entrypoint` | string | The path to the HTML entry point for your application. The `entrypoint` field specifies the path to your application's entry point. For example: <ul><li><code>"dist/index.html"</code>: Static content rooted at the `dist` directory</li><li><code>"dist/foo.html"</code>: Static content rooted at the `dist` directory, with `foo.html` as the entry point</li><li><code>"dist/"</code>: Static content rooted at the `dist` directory (assumes `dist/index.html` exists)</li><li><code>"dist/bar/foo.html"</code>: Static content rooted at `dist/bar` with `foo.html` as the entry point</li></ul> | | ||
|
||
{{% /tablestep %}} | ||
{{% tablestep number=2 %}} | ||
**Register your module** with Viam: | ||
|
||
{{< tabs >}} | ||
{{% tab name="Template" %}} | ||
|
||
```sh {class="command-line" data-prompt="$" data-output="3-10"} | ||
viam module create --name="app-name" --public-namespace="namespace" | ||
``` | ||
|
||
{{% /tab %}} | ||
{{% tab name="Example" %}} | ||
|
||
```sh {class="command-line" data-prompt="$" data-output="3-10"} | ||
viam module create --name="air-quality" --public-namespace="naomi" | ||
``` | ||
|
||
{{% /tab %}} | ||
{{< /tabs >}} | ||
|
||
{{% /tablestep %}} | ||
{{% tablestep number=3 %}} | ||
|
||
**Package your static files and your <FILE>meta.json</FILE> file and upload them** to the Viam Registry: | ||
|
||
```sh {class="command-line" data-prompt="$" data-output="3-10"} | ||
tar -czvf module.tar.gz <static-website-files> meta.json | ||
viam module upload --upload=module.tar.gz --platform=any --version=0.0.1 | ||
``` | ||
|
||
For subsequent updates run these commands again with an updated version number. | ||
|
||
{{% /tablestep %}} | ||
{{< /table >}} | ||
|
||
## Access your app | ||
|
||
After uploading your module with the application configuration, your application will be available at: | ||
|
||
```txt | ||
https://your-app-name_your-public-namespace.viamapplications.com | ||
``` | ||
|
||
Users will be prompted to authenticate with their Viam credentials before accessing your application: | ||
|
||
1. User navigates to `your-app-name_your-public-namespace.viamapplications.com`. | ||
1. User authenticates with Viam credentials. | ||
1. User selects an organization, location, and machine. | ||
1. User is redirected to `your-app-name_your-public-namespace.viamapplications.com/machine/{machine-id}`. | ||
1. Your application is rendered with access to the selected machine. | ||
|
||
## Example | ||
|
||
<!-- For a TypeScript example see [Monitor Air Quality with a Fleet of Sensors](/tutorials/control/air-quality-fleet/). --> | ||
|
||
For a React app that shows camera feeds for a machine, see [Viam Camera Viewer](https://github.com/viam-labs/viam-camera-viewer). | ||
|
||
## Limitations | ||
|
||
- Apps currently only support single-machine applications. | ||
- All modules with apps must have public visibility. | ||
- The page will always render the latest version. | ||
- Browsers with cookies disabled are not supported. | ||
- Viam apps serve static files. | ||
If you are building an app with server-side rendering or need other back-end capabilites, Viam apps is not the right choice. | ||
|
||
## Security considerations | ||
|
||
- Customer apps are stored publicly on the internet, so avoid uploading sensitive information in your application code or assets. | ||
- API keys and secrets are stored in the browser's cookies. | ||
- Users authenticate with FusionAuth. | ||
|
||
## FAQ | ||
|
||
### Can I use a custom domain? | ||
|
||
If you want to serve your side from a domain other than `your-app-name_your-public-namespace.viamapplications.com`, you can set this up with your DNS provider. | ||
|
||
To configure an apex domain (`example.com`) and the `www` subdomain, set the following values: | ||
|
||
<!-- prettier-ignore --> | ||
| Domain | DNS record type | DNS record name | DNS record value | | ||
| ------ | --------------- | --------------- | ---------------- | | ||
| `example.com` | `A` | `@` | `34.8.79.17` | | ||
| `example.com` | `ANAME` or `ALIAS` | `@` | `your-app-name_your-public-namespace.viamapplications.com` | | ||
| `www.examples.com` | `CNAME` | `SUBDOMAIN.example.com` | `your-app-name_your-public-namespace.viamapplications.com` | | ||
|
||
To configure a subdomain, set the following values: | ||
|
||
<!-- prettier-ignore --> | ||
| Domain | DNS record type | DNS record name | DNS record value | | ||
| ------ | --------------- | --------------- | ---------------- | | ||
| Subdomain (`www.examples.com` or `app.example.com`) | `CNAME` | `SUBDOMAIN.example.com` | `your-app-name_your-public-namespace.viamapplications.com` | |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -987,16 +987,16 @@ Do not change the <code>module_id</code>.</p> | |||||
</tr> | ||||||
<tr> | ||||||
<td><code>models</code></td> | ||||||
<td>object</td> | ||||||
<td><strong>Required</strong></td> | ||||||
<td><p>A list of one or more {{< glossary_tooltip term_id="model" text="models" >}} provided by your custom module. You must provide at least one model, which consists of an <code>api</code> and <code>model</code> key pair. If you are publishing a public module (<code>"visibility": "public"</code>), the namespace of your model must match the <a href="/operate/reference/naming-modules/#create-a-namespace-for-your-organization">namespace of your organization</a>.</p> | ||||||
<td>array</td> | ||||||
<td>Optional</td> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
[opt] Maybe better to emphasize like this since it's not just plain optional, and this is within the integrate hardware section? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes that column too long. If anyone makes a mistake with this the CLI will tell them about it and mostly this is auto-generated, so I'm not worried that there will be confusion. |
||||||
<td><p>A list of one or more {{< glossary_tooltip term_id="model" text="models" >}} provided by your custom module. You must provide at least one model in the models array or one application in the applications array. A model consists of an <code>api</code> and <code>model</code> key pair. If you are publishing a public module (<code>"visibility": "public"</code>), the namespace of your model must match the <a href="/operate/reference/naming-modules/#create-a-namespace-for-your-organization">namespace of your organization</a>.</p> | ||||||
<p>You are strongly encouraged to include a <code>markdown_link</code> to the section of the README containing configuration information about each model, so that the section will be displayed alongside the configuration panel when configuring the model. For example, <code>"README.md#configure-your-meteo_pm-sensor"</code>. Please also include a <code>short_description</code> describing what hardware the model supports.</p></td> | ||||||
</tr> | ||||||
<tr> | ||||||
<td><code>entrypoint</code></td> | ||||||
<td>string</td> | ||||||
<td><strong>Required</strong></td> | ||||||
<td>The name of the file that starts your module program. This can be a compiled executable, a script, or an invocation of another program. If you are providing your module as a single file to the <code>upload</code> command, provide the path to that single file. If you are providing a directory containing your module to the <code>upload</code> command, provide the path to the entry point file contained within that directory.</td> | ||||||
<td>Optional</td> | ||||||
<td>The name of the file that starts your module program. This can be a compiled executable, a script, or an invocation of another program. If you are providing your module as a single file to the <code>upload</code> command, provide the path to that single file. If you are providing a directory containing your module to the <code>upload</code> command, provide the path to the entry point file contained within that directory. Required if you are shipping a model.</td> | ||||||
</tr> | ||||||
<tr> | ||||||
<td><code>build</code></td> | ||||||
|
@@ -1016,6 +1016,12 @@ Do not change the <code>module_id</code>.</p> | |||||
<td>Optional</td> | ||||||
<td>Enables VS Code hover and autocomplete as you edit your module code. Gets auto-generated when you run <code>viam module generate</code> or <code>viam module create</code>. Has no impact on the module's function.</td> | ||||||
</tr> | ||||||
<tr> | ||||||
<td><code>applications</code></td> | ||||||
<td>array</td> | ||||||
<td>Optional</td> | ||||||
<td>Objects that provide information about the [apps](/operate/control/viam-app/) provided by the module.</td> | ||||||
</tr> | ||||||
|
||||||
</table> | ||||||
|
||||||
|
Uh oh!
There was an error while loading. Please reload this page.