`;
- h += `
`;
-
- h += `
${html_encode(window.extractProtocol(window.url))}://.${html_encode(window.hosting_domain)}
`;
+
+ // Publishing options
+ h += `
`;
+ h += `
`;
+
+ // Check if user has active subscription for custom domains
+ const hasActiveSubscription = window.user && window.user.subscription && window.user.subscription.active;
+
+ // Puter subdomain option
+ h += `
`;
+ h += `
`;
+ h += `
`;
+
+ // Custom domain option
+ h += `
`;
+ const customDomainDisabled = !hasActiveSubscription;
+ const customDomainStyle = customDomainDisabled ?
+ 'display: flex; align-items: center; cursor: not-allowed; padding: 10px; border: 2px solid #e1e8ed; border-radius: 8px; opacity: 0.5; background-color: #f8f9fa;' :
+ 'display: flex; align-items: center; cursor: pointer; padding: 10px; border: 2px solid #e1e8ed; border-radius: 8px;';
+
+ h += `
`;
+ h += `
`;
h += `
`;
+
+ // Puter subdomain input (shown by default)
+ h += `
`;
+ h += `
`;
+ h += `
`;
+ h += `${html_encode(window.extractProtocol(window.url))}://`;
+ h += ``;
+ h += `.${html_encode(window.hosting_domain)}`;
+ h += `
`;
+ h += `
`;
+
+ // Custom domain input (hidden by default)
+ h += `
`;
+ h += `
`;
+ h += `
`;
+ h += `
Make sure your domain's DNS points to your hosting provider
`;
+ h += `
`;
+
// uid
h += `
`;
// Publish
@@ -67,6 +118,56 @@ async function UIWindowPublishWebsite(target_dir_uid, target_dir_name, target_di
onAppend: function(this_window){
$(this_window).find(`.publish-website-subdomain`).val(window.generate_identifier());
$(this_window).find(`.publish-website-subdomain`).get(0).focus({preventScroll:true});
+
+ // Handle radio button changes
+ $(this_window).find('input[name="publishing-type"]:not(:disabled)').on('change', function(){
+ const selectedValue = $(this).val();
+ const puterSection = $(this_window).find('.puter-subdomain-section');
+ const customSection = $(this_window).find('.custom-domain-section');
+ const puterLabel = $(this_window).find('input[value="puter"]').closest('.option-label');
+ const customLabel = $(this_window).find('input[value="custom"]').closest('.option-label');
+
+ // Update visual selection (only if not disabled)
+ puterLabel.css('border-color', selectedValue === 'puter' ? '#007bff' : '#e1e8ed');
+ if (!$(this_window).find('input[value="custom"]').is(':disabled')) {
+ customLabel.css('border-color', selectedValue === 'custom' ? '#007bff' : '#e1e8ed');
+ }
+
+ if(selectedValue === 'puter'){
+ puterSection.show();
+ customSection.hide();
+ $(this_window).find(`.publish-website-subdomain`).focus();
+ } else if (selectedValue === 'custom') {
+ puterSection.hide();
+ customSection.show();
+ $(this_window).find(`.publish-website-custom-domain`).focus();
+ }
+ });
+
+ // Add click handler for disabled custom domain option to show upgrade message
+ $(this_window).find('.custom-domain-label').on('click', function(e){
+ const radioButton = $(this).find('input[type="radio"]');
+ if (radioButton.is(':disabled')) {
+ e.preventDefault();
+ // Could show upgrade modal here in the future
+ if(puter.defaultGUIOrigin === 'https://puter.com'){
+ $(this_window).find('.publish-website-error-msg').html(
+ 'Custom domains require a Premium subscription.
Upgrade now to use your own domain name.'
+ );
+ }else{
+ $(this_window).find('.publish-website-error-msg').html(
+ 'Custom domains are not available on this instance of Puter. Yet!'
+ );
+ }
+ $(this_window).find('.publish-website-error-msg').fadeIn();
+ setTimeout(() => {
+ $(this_window).find('.publish-website-error-msg').fadeOut();
+ }, 5000);
+ }
+ });
+
+ // Style the selected option initially
+ $(this_window).find('input[value="puter"]').closest('.option-label').css('border-color', '#007bff');
},
window_class: 'window-publishWebsite',
window_css:{
@@ -80,19 +181,46 @@ async function UIWindowPublishWebsite(target_dir_uid, target_dir_name, target_di
}
})
- $(el_window).find('.publish-btn').on('click', function(e){
- // todo do some basic validation client-side
+ // Function to load Entri SDK
+ async function loadEntriSDK() {
+ if (!window.entri) {
+ await new Promise((resolve, reject) => {
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = "https://cdn.goentri.com/entri.js";
+ script.addEventListener("load", () => {
+ resolve(window.entri);
+ });
+ script.addEventListener("error", () => {
+ reject(new Error("Failed to load the Entri SDK."));
+ });
+ document.body.appendChild(script);
+ });
+ }
+ }
- //Subdomain
- let subdomain = $(el_window).find('.publish-website-subdomain').val();
-
+ $(el_window).find('.publish-btn').on('click', async function(e){
+ e.preventDefault();
+
+ // Get the selected publishing type
+ const publishingType = $(el_window).find('input[name="publishing-type"]:checked').val();
+
// disable 'Publish' button
$(el_window).find('.publish-btn').prop('disabled', true);
- puter.hosting.create(
- subdomain,
- target_dir_path).then((res)=>{
+ try {
+ if (publishingType === 'puter') {
+ // Handle Puter subdomain publishing
+ let subdomain = $(el_window).find('.publish-website-subdomain').val();
+
+ if (!subdomain.trim()) {
+ throw new Error('Please enter a subdomain name');
+ }
+
+ const res = await puter.hosting.create(subdomain, target_dir_path);
let url = 'https://' + subdomain + '.' + window.hosting_domain + '/';
+
+ // Show success
$(el_window).find('.window-publishWebsite-form').hide(100, function(){
$(el_window).find('.publishWebsite-published-link').attr('href', url);
$(el_window).find('.publishWebsite-published-link').text(url);
@@ -109,18 +237,72 @@ async function UIWindowPublishWebsite(target_dir_uid, target_dir_name, target_di
})
window.update_sites_cache();
- }).catch((err)=>{
- err = err.error;
- $(el_window).find('.publish-website-error-msg').html(
- err.message + (
- err.code === 'subdomain_limit_reached' ?
- '
' + i18n('manage_your_subdomains') + '' : ''
- )
- );
- $(el_window).find('.publish-website-error-msg').fadeIn();
- // re-enable 'Publish' button
- $(el_window).find('.publish-btn').prop('disabled', false);
- })
+
+ } else if (publishingType === 'custom') {
+ // Handle custom domain publishing with Entri
+ let customDomain = $(el_window).find('.publish-website-custom-domain').val();
+
+ if (!customDomain.trim()) {
+ throw new Error('Please enter your custom domain');
+ }
+
+ // Step 1: First create a Puter subdomain to host the content
+ let subdomain = $(el_window).find('.publish-website-subdomain').val();
+ if (!subdomain.trim()) {
+ // Generate a subdomain if not provided
+ subdomain = window.generate_identifier();
+ }
+
+ const hostingRes = await puter.hosting.create(subdomain, target_dir_path);
+ const puterSiteUrl = 'https://' + subdomain + '.' + window.hosting_domain;
+
+ // Step 2: Load Entri SDK
+ await loadEntriSDK();
+
+ // Step 3: Get Entri config from the backend using the Puter subdomain as userHostedSite
+ const entriConfig = await puter.drivers.call("entri", "entri-service", "getConfig", {
+ domain: customDomain,
+ userHostedSite: subdomain + '.' + window.hosting_domain
+ });
+
+ // Step 4: Show Entri interface for custom domain setup
+ await entri.showEntri(entriConfig.result);
+
+ // Step 5: Show success message with custom domain
+ let customUrl = 'https://' + customDomain + '/';
+
+ $(el_window).find('.window-publishWebsite-form').hide(100, function(){
+ $(el_window).find('.publishWebsite-published-link').attr('href', customUrl);
+ $(el_window).find('.publishWebsite-published-link').text(customUrl);
+ $(el_window).find('.window-publishWebsite-success').show(100)
+ $(`.item[data-uid="${target_dir_uid}"] .item-has-website-badge`).show();
+ });
+
+ // Update items to show both the Puter subdomain and custom domain
+ $(`.item[data-path^="${target_dir_path}/"]`).each(function(){
+ // show the link badge
+ $(this).find('.item-has-website-url-badge').show();
+ // update item's website_url attribute to use custom domain
+ $(this).attr('data-website_url', customUrl + $(this).attr('data-path').substring(target_dir_path.length));
+ // Also store the puter subdomain URL as backup
+ $(this).attr('data-puter_website_url', puterSiteUrl + $(this).attr('data-path').substring(target_dir_path.length));
+ })
+
+ window.update_sites_cache();
+ }
+
+ } catch (err) {
+ const errorMessage = err.message || (err.error && err.error.message) || 'An error occurred while publishing';
+ $(el_window).find('.publish-website-error-msg').html(
+ errorMessage + (
+ err.error && err.error.code === 'subdomain_limit_reached' ?
+ '
' + i18n('manage_your_subdomains') + '' : ''
+ )
+ );
+ $(el_window).find('.publish-website-error-msg').fadeIn();
+ // re-enable 'Publish' button
+ $(el_window).find('.publish-btn').prop('disabled', false);
+ }
})
$(el_window).find('.publish-window-ok-btn').on('click', function(){
diff --git a/src/gui/src/i18n/translations/en.js b/src/gui/src/i18n/translations/en.js
index 86c3125e6..7777fbed9 100644
--- a/src/gui/src/i18n/translations/en.js
+++ b/src/gui/src/i18n/translations/en.js
@@ -72,6 +72,7 @@ const en = {
confirm_delete_user_title: "Delete Account?",
confirm_session_revoke: "Are you sure you want to revoke this session?",
confirm_your_email_address: "Confirm Your Email Address",
+ choose_publishing_option: "Choose how you want to publish your website:",
contact_us: "Contact Us",
contact_us_verification_required: "You must have a verified email address to use this.",
contain: 'Contain',