Merge juancastro/put-2258-push-from-app-subdomains into juancastro/put-2262-web-push

This commit is contained in:
Juan Castro committed 2026-10-28 10:01:02 -04:00
commit 06ef9506fa
37 files changed
+1864 -201

No files matched your search

+1
View File
@@ -191,6 +191,7 @@ These hosting features are supported out of the box when using Puter.js:
- **[`puter.hosting.delete()`](/Hosting/delete/)** - Delete a hosting deployment
- **[`puter.hosting.update()`](/Hosting/update/)** - Update hosting settings
- **[`puter.hosting.get()`](/Hosting/get/)** - Get information about a specific deployment
- **[`puter.hosting.attach()`](/Hosting/attach/)** - Let the user connect their own domain to a website
## Examples
+70
View File
@@ -0,0 +1,70 @@
---
title: puter.hosting.attach()
description: Let the user connect one of their own domains to a website hosted on Puter.
platforms: [websites, apps]
---
Opens a Puter dialog where the user connects a domain they own, such as `www.example.com`, to one of their websites. In the dialog, the user can pick a domain already on their account or add a new one. A domain the user picks that currently serves another of their websites moves to this one.
When the user adds a new domain, the dialog shows the DNS records to set at their DNS provider. The domain starts serving the website once those records are in place, usually within a few minutes.
Your app doesn't need any permission to call this method. The user makes the choice in the dialog, and your app only receives the domain they chose. Custom domains are billed to the user's Puter account, and the dialog shows the price before the user adds one.
Inside Puter, the dialog is rendered by the desktop environment. On a website, a puter.com popup hosts the dialog and signs the user in first if needed.
## Syntax
```js
puter.hosting.attach(subdomain)
```
## Parameters
#### `subdomain` (String) (required)
The name of the website to connect a domain to, such as `example` for `example.puter.site`. The website must belong to the signed-in user.
## Return value
A `Promise` that resolves to a [`CustomDomain`](/Objects/customdomain/) object for the domain the user chose, or `null` if the user closed the dialog without choosing one.
A newly added domain resolves with `status` set to `pending`. Its `records` list the DNS records the user still has to set.
On a website that is cross-origin isolated, the popup can't send its result back to your page, so the promise resolves `null` even if the user goes on to connect a domain in the popup.
The promise rejects with an error whose `code` is one of the following:
- `invalid_argument` when `subdomain` is missing or empty.
- `not_available` in an environment that can't show a dialog, such as Node.js or a worker.
- `not_supported` inside a version of the Puter desktop that doesn't have the dialog.
## Examples
<strong class="example-title">Connect a custom domain to a website</strong>
```html;hosting-attach
<html>
<body>
<button id="attach">Use my own domain</button>
<script src="https://js.puter.com/v2/"></script>
<script>
document.getElementById('attach').addEventListener('click', async () => {
// (1) Create a website to connect the domain to
const site = await puter.hosting.create(puter.randName(), '.');
// (2) Let the user pick or add a domain for it
const domain = await puter.hosting.attach(site.subdomain);
if (!domain) {
puter.print('No domain was chosen.');
return;
}
puter.print(`${domain.hostname} is ${domain.status}.<br>`);
for (const record of domain.records) {
puter.print(`${record.type} ${record.name} → ${record.value}<br>`);
}
});
</script>
</body>
</html>
```
+1
View File
@@ -12,6 +12,7 @@ Various object types and classes that represent different entities in the Puter
- **[ChatResponse](/Objects/chatresponse/)** - Represents an AI chat response
- **[ChatResponseChunk](/Objects/chatresponsechunk/)** - Represents a chunk of streaming chat response data
- **[CreateAppResult](/Objects/createappresult/)** - Represents the result of creating an app
- **[CustomDomain](/Objects/customdomain/)** - Represents a domain connected to a website
- **[DetailedAppUsage](/Objects/detailedappusage/)** - Represents detailed resource usage statistics for a specific application
- **[EmailListPage](/Objects/emaillistpage/)** - Represents one page of mailbox listing results
- **[EmailMessage](/Objects/emailmessage/)** - Represents a fully parsed message from the user's mailbox
+54
View File
@@ -0,0 +1,54 @@
---
title: CustomDomain
description: The CustomDomain object containing the details of a domain connected to a website.
---
The `CustomDomain` object describes a domain the user owns that serves one of their websites hosted on Puter. The [`puter.hosting.attach()`](/Hosting/attach/) method resolves to it.
## Attributes
#### `uuid` (String)
The unique identifier of the custom domain.
#### `hostname` (String)
The domain, such as `www.example.com`.
#### `site` (String)
The name of the website the domain serves, such as `example` for `example.puter.site`.
#### `status` (String)
The state of the domain. It's one of the following:
- `pending` until the DNS records are set and the domain is verified.
- `active` once the domain serves the website.
- `failed` when the domain can't be verified. The `statusDetail` attribute says why.
#### `statusDetail` (String | null)
Why the domain isn't active yet, when that's known.
#### `isApex` (Boolean)
`true` when the hostname is a root domain such as `example.com`. A root domain can't use a regular `CNAME` record, so its DNS provider has to support `CNAME` flattening, `ALIAS`, or `ANAME` records.
#### `records` (Array)
The DNS records to set at the domain's DNS provider. Each record is an object with these attributes:
- `type` (String): `CNAME` or `TXT`.
- `name` (String): the record's name.
- `value` (String): the record's value.
- `purpose` (String): `routing` for the record that sends traffic to the website, `ownership` for the record that proves the user owns the domain, or `certificate` for a record that speeds up issuing the HTTPS certificate.
- `required` (Boolean): `true` for the `routing` and `ownership` records, which the user has to set. A `certificate` record is optional.
#### `createdAt` (Number)
When the domain was added, in milliseconds since the Unix epoch.
#### `activatedAt` (Number | null)
When the domain became active, in milliseconds since the Unix epoch, or `null` if it isn't active.
+6
View File
@@ -786,6 +786,12 @@ const examples = [
slug: 'hosting-get',
source: '/playground/examples/hosting-get.html',
},
{
title: 'Connect a custom domain to a website',
description: 'Let the user connect their own domain to a website with Puter.js hosting API. Run and experiment with this example directly in the playground.',
slug: 'hosting-attach',
source: '/playground/examples/hosting-attach.html',
},
{
title: 'Publish a portfolio page',
description: 'Publish a page built from form input as a public website with Puter.js hosting API. Run and modify this example directly in your browser.',
@@ -0,0 +1,24 @@
<html>
<body>
<button id="attach">Use my own domain</button>
<script src="https://js.puter.com/v2/"></script>
<script>
document.getElementById('attach').addEventListener('click', async () => {
// (1) Create a website to connect the domain to
const site = await puter.hosting.create(puter.randName(), '.');
// (2) Let the user pick or add a domain for it
const domain = await puter.hosting.attach(site.subdomain);
if (!domain) {
puter.print('No domain was chosen.');
return;
}
puter.print(`${domain.hostname} is ${domain.status}.<br>`);
for (const record of domain.records) {
puter.print(`${record.type} ${record.name} → ${record.value}<br>`);
}
});
</script>
</body>
</html>
+15
View File
@@ -752,6 +752,14 @@ let sidebar = [
source: '/Hosting/get.md',
path: '/Hosting/get',
},
{
title: '<code>attach()</code>',
page_title: '<code>puter.hosting.attach()</code>',
title_tag: 'puter.hosting.attach()',
icon: '/assets/img/function.svg',
source: '/Hosting/attach.md',
path: '/Hosting/attach',
},
],
},
{
@@ -1548,6 +1556,13 @@ let sidebar = [
source: '/Objects/speech2txtresult.md',
path: '/Objects/speech2txtresult',
},
{
title: '<code>CustomDomain</code>',
title_tag: 'CustomDomain',
icon: '/assets/img/object.svg',
source: '/Objects/customdomain.md',
path: '/Objects/customdomain',
},
{
title: '<code>Subdomain</code>',
title_tag: 'Subdomain',