SMeetMini Apps

SMeet Mini Apps

Files

A file a person uploads is their content, not yours. It never opens as a page on your origin: the SMeet bridge trusts every document of that origin, so an uploaded HTML or SVG file shown there would act as your app.

Uploading#

  • The person chooses a file with a standard <input type="file">. Show whom it goes to: your server, not SMeet.
  • Upload it to your server with your own session, after checkSession (Checking the session). Show the progress, let the person cancel, and repeat an interrupted upload.
  • Keep the file with the draft on your server, keyed by the app, the environment, the person and the space, so that a person who opens the window again finds the file already there.
  • Keep the name the person gave only as text: strip the path and control characters, shorten it, and show it with textContent, never as markup.

Giving files back#

A file of a person goes back only as a download, never shown in the page's place:

HTTP
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="report.pdf"; filename*=UTF-8''report.pdf
X-Content-Type-Options: nosniff
Content-Security-Policy: sandbox
  • Content-Type: application/octet-stream whatever the type the browser declared at upload: the declared type is the person's word, and text/html or image/svg+xml would run as a page on your origin.
  • Content-Disposition: attachment always, never inline.
  • X-Content-Type-Options: nosniff, so that the browser does not guess a type, and the file cannot serve as a <script> or a style either.
  • Content-Security-Policy: sandbox, so that even a file that is opened anyway runs as a document of no origin, without your app's powers.
  • Check the session before every download, and give the file only to its owner and to the people your app lets see it.

The service desk example builds the header like this:

JavaScriptdocs/mini-apps/examples/desk/lib/files.mjs
export function attachmentDisposition(name) {
  const ascii = name.replace(/[^\x20-\x7e]/g, '_').replace(/["\\;]/g, '_');
  const encoded = encodeURIComponent(name).replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
  return `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`;
}

This is rule 4.4 of the Rules for authors: no user or third-party HTML on the origin of a Mini App.

Files on another origin#

Instead, you can serve people's files from an origin of their own, one that is not the origin of the Mini App, for example https://files.example.net. That origin is never framed by SMeet and never connects to the bridge. The headers of the section above are still good practice there.

Downloads in the web window#

In the SMeet web app the window does not allow downloads: a link with download, or an answer with attachment, started from inside the window does nothing. Give the person their file another way:

  • send a copy to the chat through your bot (below): the person opens it from the chat in every app;
  • or show what the file contains, in the page, as text.

A copy in the chat#

Your server can send a file to the person through the bot: uploadFile, then, once the file is checked and ready, sendDocument into chat.id with the expected_access_revision of the operation, from the outbox (The outbox). A file that fails the check (rejected, scan_failed) is a visible failure of the notice; it is not sent again with a new key. Files of bots need file support switched on in SMeet (Files in the Bot API).

Limits#

Your server sets the size of an upload. A file that goes into the chat is limited by the Bot API: up to 20 MiB for a document.