Skip to content

Commit 54aaed3

Browse files
Gui deploy workers (#1409)
* add deploy as worker context menu item * Update UIWindowPublishWorker.js * quick fix * Update new_context_menu_item.js * Show spinner when publishing workers * Improve worker default file * fix --------- Co-authored-by: ProgrammerIn-wonderland <[email protected]>
1 parent 6578d4d commit 54aaed3

File tree

6 files changed

+182
-7
lines changed

6 files changed

+182
-7
lines changed

src/gui/src/UI/UIItem.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import UIPopover from './UIPopover.js';
2525
import UIWindowEmailConfirmationRequired from './UIWindowEmailConfirmationRequired.js';
2626
import UIContextMenu from './UIContextMenu.js'
2727
import UIAlert from './UIAlert.js'
28+
import UIWindowPublishWorker from './UIWindowPublishWorker.js';
2829
import path from "../lib/path.js"
2930
import truncate_filename from '../helpers/truncate_filename.js';
3031
import launch_app from "../helpers/launch_app.js"
@@ -1137,6 +1138,17 @@ function UIItem(options){
11371138
});
11381139

11391140
}
1141+
//-------------------------------------------
1142+
// Publish as Worker
1143+
// -------------------------------------------
1144+
if(!is_trashed && !is_trash && !options.is_dir && $(el_item).attr('data-name').toLowerCase().endsWith('.js')){
1145+
menu_items.push({
1146+
html: i18n('publish_as_serverless_worker'),
1147+
onClick: async function(){
1148+
UIWindowPublishWorker(options.uid, $(el_item).attr('data-name'), $(el_item).attr('data-path'));
1149+
}
1150+
});
1151+
}
11401152
// -------------------------------------------
11411153
// Deploy As App
11421154
// -------------------------------------------
@@ -1158,7 +1170,6 @@ function UIItem(options){
11581170

11591171
menu_items.push('-');
11601172
}
1161-
11621173
// -------------------------------------------
11631174
// Empty Trash
11641175
// -------------------------------------------
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Copyright (C) 2024-present Puter Technologies Inc.
3+
*
4+
* This file is part of Puter.
5+
*
6+
* Puter is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as published
8+
* by the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
import UIWindow from './UIWindow.js'
21+
import UIWindowMyWebsites from './UIWindowMyWebsites.js'
22+
23+
async function UIWindowPublishWorker(target_dir_uid, target_dir_name, target_dir_path){
24+
let h = '';
25+
h += `<div class="window-publishWorker-content" style="padding: 20px; border-bottom: 1px solid #ced7e1;">`;
26+
// success
27+
h += `<div class="window-publishWorker-success">`;
28+
h += `<img src="${html_encode(window.icons['c-check.svg'])}" style="width:80px; height:80px; display: block; margin:10px auto;">`;
29+
h += `<p style="text-align:center;">${i18n('dir_published_as_website', `<strong>${html_encode(target_dir_name)}</strong>`, false)}<p>`;
30+
h += `<p style="text-align:center;"><a class="publishWorker-published-link" target="_blank"></a><img class="publishWorker-published-link-icon" src="${html_encode(window.icons['launch.svg'])}"></p>`;
31+
h += `<button class="button button-normal button-block button-primary publish-window-ok-btn" style="margin-top:20px;">${i18n('ok')}</button>`;
32+
h+= `</div>`;
33+
// form
34+
h += `<form class="window-publishWorker-form">`;
35+
// error msg
36+
h += `<div class="publish-worker-error-msg"></div>`;
37+
// worker name
38+
h += `<div style="overflow: hidden;">`;
39+
h += `<label style="margin-bottom: 10px;">${i18n('pick_name_for_worker')}</label>`;
40+
h += `<div style="font-family: monospace;">${html_encode(window.extractProtocol(window.url))}://<input class="publish-worker-name" style="width:235px;" type="text" autocomplete="subdomain" spellcheck="false" autocorrect="off" autocapitalize="off" data-gramm_editor="false"/>${html_encode('.puter.work')}</div>`;
41+
h += `</div>`;
42+
// uid
43+
h += `<input class="publishWebsiteTargetDirUID" type="hidden" value="${html_encode(target_dir_uid)}"/>`;
44+
// Publish
45+
h += `<button class="publish-btn button button-action button-block button-normal">${i18n('publish')}</button>`
46+
h += `</form>`;
47+
h += `</div>`;
48+
49+
const el_window = await UIWindow({
50+
title: i18n('window_title_publish_worker'),
51+
icon: null,
52+
uid: null,
53+
is_dir: false,
54+
body_content: h,
55+
has_head: true,
56+
selectable_body: false,
57+
draggable_body: false,
58+
allow_context_menu: false,
59+
is_resizable: false,
60+
is_droppable: false,
61+
init_center: true,
62+
allow_native_ctxmenu: true,
63+
allow_user_select: true,
64+
width: 450,
65+
dominant: true,
66+
onAppend: function(this_window){
67+
$(this_window).find(`.publish-worker-name`).val(window.generate_identifier());
68+
$(this_window).find(`.publish-worker-name`).get(0).focus({preventScroll:true});
69+
},
70+
window_class: 'window-publishWorker',
71+
window_css:{
72+
height: 'initial'
73+
},
74+
body_css: {
75+
width: 'initial',
76+
height: '100%',
77+
'background-color': 'rgb(245 247 249)',
78+
'backdrop-filter': 'blur(3px)',
79+
}
80+
})
81+
82+
$(el_window).find('.publish-btn').on('click', function(e){
83+
// todo do some basic validation client-side
84+
85+
//Worker name
86+
let worker_name = $(el_window).find('.publish-worker-name').val();
87+
88+
// Store original text and replace with spinner
89+
const originalText = $(el_window).find('.publish-btn').text();
90+
$(el_window).find('.publish-btn').prop('disabled', true).html(`
91+
<div style="display: inline-block; margin-top: 10px; width: 16px; height: 16px; border: 2px solid #ffffff; border-radius: 50%; border-top: 2px solid transparent; animation: spin 1s linear infinite;"></div>
92+
`);
93+
94+
puter.workers.create(
95+
worker_name,
96+
target_dir_path).then((res)=>{
97+
let url = 'https://' + worker_name + '.puter.work';
98+
$(el_window).find('.window-publishWorker-form').hide(100, function(){
99+
$(el_window).find('.publishWorker-published-link').attr('href', url);
100+
$(el_window).find('.publishWorker-published-link').text(url);
101+
$(el_window).find('.window-publishWorker-success').show(100)
102+
$(`.item[data-uid="${target_dir_uid}"] .item-has-website-badge`).show();
103+
});
104+
105+
// find all items whose path starts with target_dir_path
106+
$(`.item[data-path^="${target_dir_path}/"]`).each(function(){
107+
// show the link badge
108+
$(this).find('.item-has-website-url-badge').show();
109+
// update item's website_url attribute
110+
$(this).attr('data-website_url', url + $(this).attr('data-path').substring(target_dir_path.length));
111+
})
112+
}).catch((err)=>{
113+
err = err.error;
114+
$(el_window).find('.publish-worker-error-msg').html(
115+
err.message + (
116+
err.code === 'subdomain_limit_reached' ?
117+
' <span class="manage-your-websites-link">' + i18n('manage_your_subdomains') + '</span>' : ''
118+
)
119+
);
120+
$(el_window).find('.publish-worker-error-msg').fadeIn();
121+
// re-enable 'Publish' button and restore original text
122+
$(el_window).find('.publish-btn').prop('disabled', false).text(originalText);
123+
})
124+
})
125+
126+
$(el_window).find('.publish-window-ok-btn').on('click', function(){
127+
$(el_window).close();
128+
})
129+
}
130+
131+
$(document).on('click', '.manage-your-websites-link', async function(e){
132+
UIWindowMyWebsites();
133+
})
134+
135+
136+
export default UIWindowPublishWorker

src/gui/src/css/style.css

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,7 +1900,7 @@ label {
19001900
}
19011901
/***************************************************/
19021902

1903-
.login-error-msg, .signup-error-msg, .publish-website-error-msg, .form-error-msg {
1903+
.login-error-msg, .signup-error-msg, .publish-website-error-msg, .form-error-msg, .publish-worker-error-msg {
19041904
display: none;
19051905
color: red;
19061906
border: 1px solid red;
@@ -1937,7 +1937,7 @@ label {
19371937
margin-top: 20px;
19381938
}
19391939

1940-
.window-publishWebsite-success, .window-give-item-access-success {
1940+
.window-publishWebsite-success, .window-give-item-access-success, .window-publishWorker-success {
19411941
display: none;
19421942
-webkit-font-smoothing: antialiased;
19431943
-moz-osx-font-smoothing: grayscale;
@@ -1950,16 +1950,16 @@ label {
19501950
cursor: pointer;
19511951
}
19521952

1953-
.publishWebsite-published-link {
1953+
.publishWebsite-published-link, .publishWorker-published-link {
19541954
text-decoration: none;
19551955
color: #007cff;
19561956
}
19571957

1958-
.publishWebsite-published-link:hover {
1958+
.publishWebsite-published-link:hover, .publishWorker-published-link:hover {
19591959
text-decoration: underline;
19601960
}
19611961

1962-
.publishWebsite-published-link-icon {
1962+
.publishWebsite-published-link-icon, .publishWorker-published-link-icon {
19631963
display: inline-block;
19641964
width: 12px;
19651965
margin-left: 5px;

src/gui/src/helpers/new_context_menu_item.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,30 @@ const new_context_menu_item = function(dirname, append_to_element){
155155
});
156156
}
157157
},
158+
// Worker
159+
{
160+
html: i18n('worker'),
161+
icon: `<img src="${html_encode(window.icons['file-js.svg'])}" class="ctx-item-icon">`,
162+
onClick: async function() {
163+
await window.create_file({
164+
dirname: dirname,
165+
append_to_element: append_to_element,
166+
name: 'New Worker.js',
167+
content: `// This is an example application for Puter Workers
168+
169+
router.get('/', ({request}) => {
170+
return 'Hello World'; // returns a string
171+
});
172+
router.get('/api/hello', ({request}) => {
173+
return {'msg': 'hello'}; // returns a JSON object
174+
});
175+
router.get('/*page', ({request, params}) => {
176+
return new Response(\`Page \${params.page} not found\`, {status: 404});
177+
});
178+
`
179+
});
180+
}
181+
}
158182
];
159183

160184
//Show file_templates on the lower part of "New"

src/gui/src/i18n/translations/en.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ const en = {
211211
path: 'Path',
212212
personalization: "Personalization",
213213
pick_name_for_website: "Pick a name for your website:",
214+
pick_name_for_worker: "Pick a name for your worker:",
214215
picture: "Picture",
215216
pictures: 'Pictures',
216217
plural_suffix: 's',
@@ -230,6 +231,7 @@ const en = {
230231
public: 'Public',
231232
publish: "Publish",
232233
publish_as_website: 'Publish as website',
234+
publish_as_serverless_worker: 'Publish as Worker',
233235
puter_description: `Puter is a privacy-first personal cloud to keep all your files, apps, and games in one secure place, accessible from anywhere at any time.`,
234236
reading: "Reading %strong%",
235237
writing: "Writing %strong%",
@@ -331,6 +333,7 @@ const en = {
331333
you_have_been_referred_to_puter_by_a_friend: "You have been referred to Puter by a friend!",
332334
zip: "Zip",
333335
sequencing: "Sequencing %strong%",
336+
worker: "Worker",
334337
zipping: "Zipping %strong%",
335338

336339
// === 2FA Setup ===
@@ -461,6 +464,7 @@ const en = {
461464
'window_title_set_new_password': 'Set New Password',
462465
'window_title_instant_login': 'Instant Login!',
463466
'window_title_publish_website': 'Publish Website',
467+
'window_title_publish_worker': 'Publish Worker',
464468
'window_title_authenticating': 'Authenticating...',
465469
'window_title_refer_friend': 'Refer a friend!',
466470

src/puter-js/src/modules/Workers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export class WorkersHandler {
9999
if (!driverCall.result.result) {
100100
new Error("Worker doesn't exist");
101101
}
102-
throw driverCall.error || new Error(driverResult?.errors || "Driver failed to execute, do you have the necessary permissions?");
102+
throw driverCall.error || new Error(driverCall.result?.errors || "Driver failed to execute, do you have the necessary permissions?");
103103
} else {
104104
let currentWorkers = await puter.kv.get("user-workers");
105105

0 commit comments

Comments
 (0)