mirror of
https://github.com/HeyPuter/puter.git
synced 2026-05-03 13:40:50 -05:00
fix: adding more benign changes (#1891)
This commit is contained in:
+102
-62
@@ -1,27 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { AdvancedBase } = require("@heyputer/putility");
|
||||
const EmitterFeature = require("@heyputer/putility/src/features/EmitterFeature");
|
||||
const { Context } = require("./util/context");
|
||||
const { ExtensionServiceState } = require("./ExtensionService");
|
||||
const { display_time } = require("@heyputer/putility/src/libs/time");
|
||||
const { AdvancedBase } = require('@heyputer/putility');
|
||||
const EmitterFeature = require('@heyputer/putility/src/features/EmitterFeature');
|
||||
const { Context } = require('./util/context');
|
||||
const { ExtensionServiceState } = require('./ExtensionService');
|
||||
const { display_time } = require('@heyputer/putility/src/libs/time');
|
||||
|
||||
/**
|
||||
* This class creates the `extension` global that is seen by Puter backend
|
||||
@@ -33,11 +33,11 @@ class Extension extends AdvancedBase {
|
||||
decorators: [
|
||||
fn => Context.get(undefined, {
|
||||
allow_fallback: true,
|
||||
}).abind(fn)
|
||||
]
|
||||
}).abind(fn),
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
|
||||
randomBrightColor() {
|
||||
// Bright colors in ANSI (foreground codes 90–97)
|
||||
const brightColors = [
|
||||
@@ -52,30 +52,30 @@ class Extension extends AdvancedBase {
|
||||
return brightColors[Math.floor(Math.random() * brightColors.length)];
|
||||
}
|
||||
|
||||
constructor (...a) {
|
||||
constructor(...a) {
|
||||
super(...a);
|
||||
this.service = null;
|
||||
this.log = null;
|
||||
this.ensure_service_();
|
||||
|
||||
|
||||
// this.terminal_color = this.randomBrightColor();
|
||||
this.terminal_color = 94;
|
||||
|
||||
|
||||
this.log = (...a) => {
|
||||
this.log_context.info(a.join(' '));
|
||||
};
|
||||
this.LOG = (...a) => {
|
||||
this.log_context.noticeme(a.join(' '));
|
||||
};
|
||||
['info','warn','debug','error','tick','noticeme','system'].forEach(lvl => {
|
||||
['info', 'warn', 'debug', 'error', 'tick', 'noticeme', 'system'].forEach(lvl => {
|
||||
this.log[lvl] = (...a) => {
|
||||
this.log_context[lvl](...a);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
this.only_one_preinit_fn = null;
|
||||
this.only_one_init_fn = null;
|
||||
|
||||
|
||||
this.registry = {
|
||||
register: this.register.bind(this),
|
||||
of: (typeKey) => {
|
||||
@@ -90,23 +90,23 @@ class Extension extends AdvancedBase {
|
||||
...Object.values(this.registry_[typeKey].named),
|
||||
...this.registry_[typeKey].anonymous,
|
||||
],
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
example () {
|
||||
example() {
|
||||
console.log('Example method called by an extension.');
|
||||
}
|
||||
|
||||
|
||||
// === [START] RuntimeModule aliases ===
|
||||
set exports (value) {
|
||||
set exports(value) {
|
||||
this.runtime.exports = value;
|
||||
}
|
||||
get exports () {
|
||||
get exports() {
|
||||
return this.runtime.exports;
|
||||
}
|
||||
import (name) {
|
||||
import(name) {
|
||||
return this.runtime.import(name);
|
||||
}
|
||||
// === [END] RuntimeModule aliases ===
|
||||
@@ -114,59 +114,53 @@ class Extension extends AdvancedBase {
|
||||
/**
|
||||
* This will get a database instance from the default service.
|
||||
*/
|
||||
get db () {
|
||||
get db() {
|
||||
const db = this.service.values.get('db');
|
||||
if ( ! db ) {
|
||||
throw new Error(
|
||||
'extension tried to access database before it was ' +
|
||||
'initialized'
|
||||
);
|
||||
throw new Error('extension tried to access database before it was ' +
|
||||
'initialized');
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
get services () {
|
||||
get services() {
|
||||
const services = this.service.values.get('services');
|
||||
if ( ! services ) {
|
||||
throw new Error(
|
||||
'extension tried to access "services" before it was ' +
|
||||
'initialized'
|
||||
);
|
||||
throw new Error('extension tried to access "services" before it was ' +
|
||||
'initialized');
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
get log_context () {
|
||||
get log_context() {
|
||||
const log_context = this.service.values.get('log_context');
|
||||
if ( ! log_context ) {
|
||||
throw new Error(
|
||||
'extension tried to access "log_context" before it was ' +
|
||||
'initialized'
|
||||
);
|
||||
throw new Error('extension tried to access "log_context" before it was ' +
|
||||
'initialized');
|
||||
}
|
||||
return log_context;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register anonymous or named data to a particular type/category.
|
||||
* @param {string} typeKey Type of data being registered
|
||||
* @param {string} [key] Key of data being registered
|
||||
* @param {any} data The data to be registered
|
||||
*/
|
||||
register (typeKey, keyOrData, data) {
|
||||
register(typeKey, keyOrData, data) {
|
||||
if ( ! this.registry_[typeKey] ) {
|
||||
this.registry_[typeKey] = {
|
||||
named: {},
|
||||
anonymous: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const typeRegistry = this.registry_[typeKey];
|
||||
|
||||
|
||||
if ( arguments.length <= 1 ) {
|
||||
throw new Error('you must specify what to register');
|
||||
}
|
||||
|
||||
|
||||
if ( arguments.length === 2 ) {
|
||||
data = keyOrData;
|
||||
if ( Array.isArray(data) ) {
|
||||
@@ -178,28 +172,28 @@ class Extension extends AdvancedBase {
|
||||
typeRegistry.anonymous.push(data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const key = keyOrData;
|
||||
typeRegistry.named[key] = data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Alias for .register()
|
||||
* @param {string} typeKey Type of data being registered
|
||||
* @param {string} [key] Key of data being registered
|
||||
* @param {any} data The data to be registered
|
||||
*/
|
||||
reg (...a) {
|
||||
reg(...a) {
|
||||
this.register(...a);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will create a GET endpoint on the default service.
|
||||
* @param {*} path - route for the endpoint
|
||||
* @param {*} handler - function to handle the endpoint
|
||||
* @param {*} options - options like noauth (bool) and mw (array)
|
||||
*/
|
||||
get (path, handler, options) {
|
||||
get(path, handler, options) {
|
||||
// this extension will have a default service
|
||||
this.ensure_service_();
|
||||
|
||||
@@ -221,7 +215,7 @@ class Extension extends AdvancedBase {
|
||||
* @param {*} handler - function to handle the endpoint
|
||||
* @param {*} options - options like noauth (bool) and mw (array)
|
||||
*/
|
||||
post (path, handler, options) {
|
||||
post(path, handler, options) {
|
||||
// this extension will have a default service
|
||||
this.ensure_service_();
|
||||
|
||||
@@ -236,8 +230,52 @@ class Extension extends AdvancedBase {
|
||||
methods: ['POST'],
|
||||
});
|
||||
}
|
||||
|
||||
use (...args) {
|
||||
|
||||
/**
|
||||
* This will create a DELETE endpoint on the default service.
|
||||
* @param {*} path - route for the endpoint
|
||||
* @param {*} handler - function to handle the endpoint
|
||||
* @param {*} options - options like noauth (bool) and mw (array)
|
||||
*/
|
||||
put(path, handler, options) {
|
||||
// this extension will have a default service
|
||||
this.ensure_service_();
|
||||
|
||||
// handler and options may be flipped
|
||||
if ( typeof handler === 'object' ) {
|
||||
[handler, options] = [options, handler];
|
||||
}
|
||||
if ( ! options ) options = {};
|
||||
|
||||
this.service.register_route_handler_(path, handler, {
|
||||
...options,
|
||||
methods: ['PUT'],
|
||||
});
|
||||
}
|
||||
/**
|
||||
* This will create a DELETE endpoint on the default service.
|
||||
* @param {*} path - route for the endpoint
|
||||
* @param {*} handler - function to handle the endpoint
|
||||
* @param {*} options - options like noauth (bool) and mw (array)
|
||||
*/
|
||||
|
||||
delete(path, handler, options) {
|
||||
// this extension will have a default service
|
||||
this.ensure_service_();
|
||||
|
||||
// handler and options may be flipped
|
||||
if ( typeof handler === 'object' ) {
|
||||
[handler, options] = [options, handler];
|
||||
}
|
||||
if ( ! options ) options = {};
|
||||
|
||||
this.service.register_route_handler_(path, handler, {
|
||||
...options,
|
||||
methods: ['DELETE'],
|
||||
});
|
||||
}
|
||||
|
||||
use(...args) {
|
||||
this.ensure_service_();
|
||||
this.service.expressThings_.push({
|
||||
type: 'router',
|
||||
@@ -246,7 +284,7 @@ class Extension extends AdvancedBase {
|
||||
}
|
||||
|
||||
get preinit() {
|
||||
return (function (callback) {
|
||||
return (function(callback) {
|
||||
this.on('preinit', callback);
|
||||
}).bind(this);
|
||||
}
|
||||
@@ -257,7 +295,8 @@ class Extension extends AdvancedBase {
|
||||
});
|
||||
}
|
||||
if ( callback === null ) {
|
||||
this.only_one_preinit_fn = () => {};
|
||||
this.only_one_preinit_fn = () => {
|
||||
};
|
||||
}
|
||||
this.only_one_preinit_fn = callback;
|
||||
}
|
||||
@@ -267,19 +306,20 @@ class Extension extends AdvancedBase {
|
||||
this.on('init', callback);
|
||||
}).bind(this);
|
||||
}
|
||||
set init (callback) {
|
||||
set init(callback) {
|
||||
if ( this.only_one_init_fn === null ) {
|
||||
this.on('init', (...a) => {
|
||||
this.only_one_init_fn(...a);
|
||||
});
|
||||
}
|
||||
if ( callback === null ) {
|
||||
this.only_one_init_fn = () => {};
|
||||
this.only_one_init_fn = () => {
|
||||
};
|
||||
}
|
||||
this.only_one_init_fn = callback;
|
||||
}
|
||||
|
||||
get console () {
|
||||
get console() {
|
||||
const extensionConsole = Object.create(console);
|
||||
const logfn = level => (...a) => {
|
||||
let svc_log;
|
||||
@@ -317,10 +357,10 @@ class Extension extends AdvancedBase {
|
||||
* This method will create the "default service" for an extension.
|
||||
* This is specifically for Puter extensions that do not define their
|
||||
* own service classes.
|
||||
*
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ensure_service_ () {
|
||||
ensure_service_() {
|
||||
if ( this.service ) {
|
||||
return;
|
||||
}
|
||||
@@ -333,4 +373,4 @@ class Extension extends AdvancedBase {
|
||||
|
||||
module.exports = {
|
||||
Extension,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -626,7 +626,7 @@ class WebServerService extends BaseService {
|
||||
|
||||
const allowed_headers = [
|
||||
"Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization", "sentry-trace", "baggage",
|
||||
"Depth", "Destination", "Overwrite", "If", "Lock-Token", "DAV"
|
||||
"Depth", "Destination", "Overwrite", "If", "Lock-Token", "DAV", "stripe-signature",
|
||||
];
|
||||
|
||||
// Request headers to allow
|
||||
|
||||
@@ -17,20 +17,20 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const { Actor } = require("./auth/Actor");
|
||||
const BaseService = require("./BaseService");
|
||||
const { DB_READ } = require("./database/consts");
|
||||
const { Actor } = require('./auth/Actor');
|
||||
const BaseService = require('./BaseService');
|
||||
const { DB_READ } = require('./database/consts');
|
||||
|
||||
/**
|
||||
* Get user by one of a variety of identifying properties.
|
||||
*
|
||||
*
|
||||
* Pass `cached: false` to options to force a database read.
|
||||
* Pass `force: true` to options to force a primary database read.
|
||||
*
|
||||
*
|
||||
* This provides the functionality of `get_user` (helpers.js)
|
||||
* as a service so that other services can register identifying
|
||||
* properties for caching.
|
||||
*
|
||||
*
|
||||
* The original `get_user` function now uses this service.
|
||||
*/
|
||||
class GetUserService extends BaseService {
|
||||
@@ -38,7 +38,7 @@ class GetUserService extends BaseService {
|
||||
* Constructor for GetUserService.
|
||||
* Initializes the set of identifying properties used to retrieve user data.
|
||||
*/
|
||||
_construct () {
|
||||
_construct() {
|
||||
this.id_properties = new Set();
|
||||
|
||||
this.id_properties.add('username');
|
||||
@@ -52,35 +52,35 @@ class GetUserService extends BaseService {
|
||||
* Initializes the GetUserService instance.
|
||||
* This method prepares any necessary internal structures or states.
|
||||
* It is called automatically upon instantiation of the service.
|
||||
*
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves when the initialization is complete.
|
||||
*/
|
||||
async _init () {
|
||||
async _init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user object based on the provided options.
|
||||
*
|
||||
*
|
||||
* This method queries the user from cache or database,
|
||||
* depending on the caching options provided. If the user
|
||||
* is found, it also calls the 'whoami' service to enrich
|
||||
* depending on the caching options provided. If the user
|
||||
* is found, it also calls the 'whoami' service to enrich
|
||||
* the user details before returning.
|
||||
*
|
||||
*
|
||||
* @param {Object} options - The options for retrieving the user.
|
||||
* @param {boolean} [options.cached=true] - Indicates if caching should be used.
|
||||
* @param {boolean} [options.force=false] - Forces a read from the database regardless of cache.
|
||||
* @returns {Promise<Object|null>} The user object if found, else null.
|
||||
*/
|
||||
async get_user (options) {
|
||||
async get_user(options) {
|
||||
const user = await this.get_user_(options);
|
||||
if ( ! user ) return null;
|
||||
|
||||
|
||||
const svc_whoami = this.services.get('whoami');
|
||||
await svc_whoami.get_details({ user }, user);
|
||||
return user;
|
||||
}
|
||||
|
||||
async refresh_actor (actor) {
|
||||
|
||||
async refresh_actor(actor) {
|
||||
if ( actor.type.user ) {
|
||||
actor.type.user = await this.get_user({
|
||||
username: actor.type.user.username,
|
||||
@@ -90,7 +90,7 @@ class GetUserService extends BaseService {
|
||||
return actor;
|
||||
}
|
||||
|
||||
async get_user_ (options) {
|
||||
async get_user_(options) {
|
||||
const services = this.services;
|
||||
|
||||
/** @type BaseDatabaseAccessService */
|
||||
@@ -135,13 +135,19 @@ class GetUserService extends BaseService {
|
||||
kv.set(`users:${prop}:${user[prop]}`, user);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch ( e ) {
|
||||
console.error(e);
|
||||
}
|
||||
if ( user.metadata && typeof user.metadata === 'string' ) {
|
||||
user.metadata = JSON.parse(user.metadata);
|
||||
} else if ( !user.metadata ) {
|
||||
user.metadata = {};
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
register_id_property (prop) {
|
||||
|
||||
register_id_property(prop) {
|
||||
this.id_properties.add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { RootNodeSelector, NodeChildSelector } = require("../filesystem/node/selectors");
|
||||
const { invalidate_cached_user } = require("../helpers");
|
||||
const BaseService = require("./BaseService");
|
||||
const { DB_WRITE } = require("./database/consts");
|
||||
const { RootNodeSelector, NodeChildSelector } = require('../filesystem/node/selectors');
|
||||
const { invalidate_cached_user } = require('../helpers');
|
||||
const BaseService = require('./BaseService');
|
||||
const { DB_WRITE } = require('./database/consts');
|
||||
|
||||
class UserService extends BaseService {
|
||||
static MODULES = {
|
||||
uuidv4: require('uuid').v4,
|
||||
};
|
||||
|
||||
async _init () {
|
||||
async _init() {
|
||||
this.db = this.services.get('database').get(DB_WRITE, 'user-service');
|
||||
this.dir_system = null;
|
||||
}
|
||||
|
||||
async ['__on_filesystem.ready'] () {
|
||||
async ['__on_filesystem.ready']() {
|
||||
const svc_fs = this.services.get('filesystem');
|
||||
// Ensure system user has a home directory
|
||||
const dir_system = await svc_fs.node(
|
||||
new NodeChildSelector(
|
||||
new RootNodeSelector(),
|
||||
'system'
|
||||
)
|
||||
);
|
||||
const dir_system = await svc_fs.node(new NodeChildSelector(new RootNodeSelector(),
|
||||
'system'));
|
||||
|
||||
if ( ! await dir_system.exists() ) {
|
||||
const svc_getUser = this.services.get('get-user');
|
||||
await this.generate_default_fsentries({
|
||||
user: await svc_getUser.get_user({ username: 'system' })
|
||||
user: await svc_getUser.get_user({ username: 'system' }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,15 +50,15 @@ class UserService extends BaseService {
|
||||
this.services.emit('user.system-user-ready');
|
||||
}
|
||||
|
||||
get_system_dir () {
|
||||
get_system_dir() {
|
||||
return this.dir_system;
|
||||
}
|
||||
|
||||
// used to be called: generate_system_fsentries
|
||||
async generate_default_fsentries ({ user }) {
|
||||
|
||||
async generate_default_fsentries({ user }) {
|
||||
|
||||
this.log.noticeme('YES THIS WAS USED');
|
||||
|
||||
|
||||
// Note: The comment below is outdated as we now do parallel writes for
|
||||
// all filesystem operations. However, there may still be some
|
||||
// performance hit so this requires further investigation.
|
||||
@@ -74,7 +70,7 @@ class UserService extends BaseService {
|
||||
// by combining as many queries as we can into one and avoiding multiple back-and-forth
|
||||
// with the DB server, we can speed this process up significantly.
|
||||
|
||||
const ts = Date.now()/1000;
|
||||
const ts = Date.now() / 1000;
|
||||
|
||||
// Generate UUIDs for all the default folders and files
|
||||
const uuidv4 = this.modules.uuidv4;
|
||||
@@ -88,8 +84,7 @@ class UserService extends BaseService {
|
||||
let videos_uuid = uuidv4();
|
||||
let public_uuid = uuidv4();
|
||||
|
||||
const insert_res = await this.db.write(
|
||||
`INSERT INTO fsentries
|
||||
const insert_res = await this.db.write(`INSERT INTO fsentries
|
||||
(uuid, parent_uid, user_id, name, path, is_dir, created, modified, immutable) VALUES
|
||||
( ?, ?, ?, ?, ?, true, ?, ?, true),
|
||||
( ?, ?, ?, ?, ?, true, ?, ?, true),
|
||||
@@ -100,25 +95,24 @@ class UserService extends BaseService {
|
||||
( ?, ?, ?, ?, ?, true, ?, ?, true),
|
||||
( ?, ?, ?, ?, ?, true, ?, ?, true)
|
||||
`,
|
||||
[
|
||||
// Home
|
||||
home_uuid, null, user.id, user.username, `/${user.username}`, ts, ts,
|
||||
// Trash
|
||||
trash_uuid, home_uuid, user.id, 'Trash', `/${user.username}/Trash`, ts, ts,
|
||||
// AppData
|
||||
appdata_uuid, home_uuid, user.id, 'AppData', `/${user.username}/AppData`, ts, ts,
|
||||
// Desktop
|
||||
desktop_uuid, home_uuid, user.id, 'Desktop', `/${user.username}/Desktop`, ts, ts,
|
||||
// Documents
|
||||
documents_uuid, home_uuid, user.id, 'Documents', `/${user.username}/Documents`, ts, ts,
|
||||
// Pictures
|
||||
pictures_uuid, home_uuid, user.id, 'Pictures', `/${user.username}/Pictures`, ts, ts,
|
||||
// Videos
|
||||
videos_uuid, home_uuid, user.id, 'Videos', `/${user.username}/Videos`, ts, ts,
|
||||
// Public
|
||||
public_uuid, home_uuid, user.id, 'Public', `/${user.username}/Public`, ts, ts,
|
||||
]
|
||||
);
|
||||
[
|
||||
// Home
|
||||
home_uuid, null, user.id, user.username, `/${user.username}`, ts, ts,
|
||||
// Trash
|
||||
trash_uuid, home_uuid, user.id, 'Trash', `/${user.username}/Trash`, ts, ts,
|
||||
// AppData
|
||||
appdata_uuid, home_uuid, user.id, 'AppData', `/${user.username}/AppData`, ts, ts,
|
||||
// Desktop
|
||||
desktop_uuid, home_uuid, user.id, 'Desktop', `/${user.username}/Desktop`, ts, ts,
|
||||
// Documents
|
||||
documents_uuid, home_uuid, user.id, 'Documents', `/${user.username}/Documents`, ts, ts,
|
||||
// Pictures
|
||||
pictures_uuid, home_uuid, user.id, 'Pictures', `/${user.username}/Pictures`, ts, ts,
|
||||
// Videos
|
||||
videos_uuid, home_uuid, user.id, 'Videos', `/${user.username}/Videos`, ts, ts,
|
||||
// Public
|
||||
public_uuid, home_uuid, user.id, 'Public', `/${user.username}/Public`, ts, ts,
|
||||
]);
|
||||
|
||||
// https://stackoverflow.com/a/50103616
|
||||
let trash_id = insert_res.insertId;
|
||||
@@ -135,20 +129,29 @@ class UserService extends BaseService {
|
||||
|
||||
// TODO: pass to IIAFE manager to avoid unhandled promise rejection
|
||||
// (IIAFE manager doesn't exist yet, hence this is a TODO)
|
||||
this.db.write(
|
||||
`UPDATE user SET
|
||||
this.db.write(`UPDATE user SET
|
||||
trash_uuid=?, appdata_uuid=?, desktop_uuid=?, documents_uuid=?, pictures_uuid=?, videos_uuid=?, public_uuid=?,
|
||||
trash_id=?, appdata_id=?, desktop_id=?, documents_id=?, pictures_id=?, videos_id=?, public_id=?
|
||||
|
||||
WHERE id=?`,
|
||||
[
|
||||
trash_uuid, appdata_uuid, desktop_uuid, documents_uuid, pictures_uuid, videos_uuid, public_uuid,
|
||||
trash_id, appdata_id, desktop_id, documents_id, pictures_id, videos_id, public_id,
|
||||
user.id
|
||||
]
|
||||
);
|
||||
[
|
||||
trash_uuid, appdata_uuid, desktop_uuid, documents_uuid, pictures_uuid, videos_uuid, public_uuid,
|
||||
trash_id, appdata_id, desktop_id, documents_id, pictures_id, videos_id, public_id,
|
||||
user.id,
|
||||
]);
|
||||
invalidate_cached_user(user);
|
||||
}
|
||||
|
||||
async updateUserMetadata(user, updatedMetadata) {
|
||||
|
||||
let metadata = user.metadata;
|
||||
if ( !Object.keys(metadata).length ) {
|
||||
metadata = updatedMetadata;
|
||||
} else {
|
||||
metadata = { ...metadata, ...updatedMetadata };
|
||||
}
|
||||
|
||||
await this.db.write('UPDATE user SET metadata=? WHERE id=?', [metadata]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -16,34 +16,33 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const eggspress = require("../api/eggspress");
|
||||
const eggspress = require('../api/eggspress');
|
||||
|
||||
const Endpoint = function Endpoint (spec, handler) {
|
||||
const Endpoint = function Endpoint(spec, handler) {
|
||||
return {
|
||||
attach (route) {
|
||||
attach(route) {
|
||||
const eggspress_options = {
|
||||
allowedMethods: spec.methods ?? ['GET'],
|
||||
...(spec.subdomain ? { subdomain: spec.subdomain } : {}),
|
||||
...(spec.parameters ? { parameters: spec.parameters } : {}),
|
||||
...(spec.alias ? { alias: spec.alias } : {}),
|
||||
...(spec.mw ? { mw: spec.mw } : {}),
|
||||
...spec.otherOpts,
|
||||
};
|
||||
const eggspress_router = eggspress(
|
||||
spec.route,
|
||||
eggspress_options,
|
||||
handler ?? spec.handler,
|
||||
);
|
||||
const eggspress_router = eggspress(spec.route,
|
||||
eggspress_options,
|
||||
handler ?? spec.handler);
|
||||
route.use(eggspress_router);
|
||||
},
|
||||
but (newSpec) {
|
||||
but(newSpec) {
|
||||
// TODO: add merge with '$' behaviors (like config has)
|
||||
return Endpoint({
|
||||
...spec,
|
||||
...newSpec,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Endpoint,
|
||||
|
||||
+348
-217
@@ -31,45 +31,53 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.polyfills.min.js"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.css" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
.social-link{
|
||||
.social-link {
|
||||
opacity: 0.7;
|
||||
color: rgb(70, 78, 86);
|
||||
}
|
||||
.social-link:hover{
|
||||
|
||||
.social-link:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.social-link svg{
|
||||
|
||||
.social-link svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.sidebar-nav-social{
|
||||
|
||||
.sidebar-nav-social {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.sidebar-nav-social li{
|
||||
|
||||
.sidebar-nav-social li {
|
||||
display: inline;
|
||||
padding:0;
|
||||
padding: 0;
|
||||
margin-left: 20px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.sidebar-nav-social li a{
|
||||
|
||||
.sidebar-nav-social li a {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
tags{
|
||||
|
||||
tags {
|
||||
min-width: 500px;
|
||||
}
|
||||
.analytics-card{
|
||||
|
||||
.analytics-card {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
background-color: #f2f4f5eb;
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
float:left;
|
||||
float: left;
|
||||
margin-right: 23px;
|
||||
}
|
||||
.analytics-card h3{
|
||||
|
||||
.analytics-card h3 {
|
||||
color: #838383;
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -92,245 +100,368 @@
|
||||
<!-- Footer -->
|
||||
<div style="overflow: hidden; position: absolute; bottom: 0; width: 100%;">
|
||||
<ul class="sidebar-nav">
|
||||
<li class="no-hover" style="margin-left:0;"><a href="https://developer.puter.com/" class="link-to-docs" target="_blank">Developer Documentation<img src="img/external-link.svg"></a></li>
|
||||
<li class="no-hover" style="margin-left:0;"><a href="https://developer.puter.com/"
|
||||
class="link-to-docs" target="_blank">Developer Documentation<img
|
||||
src="img/external-link.svg"></a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="sidebar-nav sidebar-nav-social">
|
||||
<li class="no-hover"><a href="https://github.com/HeyPuter/puter" class="social-link" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16"> <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/> </svg></a></li>
|
||||
<li class="no-hover"><a href="https://dsc.gg/puter" class="social-link" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-discord" viewBox="0 0 16 16"> <path d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"/> </svg></a></li>
|
||||
<li class="no-hover"><a href="https://x.com/HeyPuter" class="social-link" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16"> <path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"/> </svg></a></li>
|
||||
<li class="no-hover"><a href="https://reddit.com/r/puter" class="social-link" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reddit" viewBox="0 0 16 16"> <path d="M6.167 8a.83.83 0 0 0-.83.83c0 .459.372.84.83.831a.831.831 0 0 0 0-1.661m1.843 3.647c.315 0 1.403-.038 1.976-.611a.23.23 0 0 0 0-.306.213.213 0 0 0-.306 0c-.353.363-1.126.487-1.67.487-.545 0-1.308-.124-1.671-.487a.213.213 0 0 0-.306 0 .213.213 0 0 0 0 .306c.564.563 1.652.61 1.977.61zm.992-2.807c0 .458.373.83.831.83s.83-.381.83-.83a.831.831 0 0 0-1.66 0z"/> <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.828-1.165c-.315 0-.602.124-.812.325-.801-.573-1.9-.945-3.121-.993l.534-2.501 1.738.372a.83.83 0 1 0 .83-.869.83.83 0 0 0-.744.468l-1.938-.41a.2.2 0 0 0-.153.028.2.2 0 0 0-.086.134l-.592 2.788c-1.24.038-2.358.41-3.17.992-.21-.2-.496-.324-.81-.324a1.163 1.163 0 0 0-.478 2.224q-.03.17-.029.353c0 1.795 2.091 3.256 4.669 3.256s4.668-1.451 4.668-3.256c0-.114-.01-.238-.029-.353.401-.181.688-.592.688-1.069 0-.65-.525-1.165-1.165-1.165"/> </svg></a></li>
|
||||
<li class="no-hover"><a href="https://github.com/HeyPuter/puter" class="social-link"
|
||||
target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8" />
|
||||
</svg></a></li>
|
||||
<li class="no-hover"><a href="https://dsc.gg/puter" class="social-link" target="_blank"><svg
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-discord" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612" />
|
||||
</svg></a></li>
|
||||
<li class="no-hover"><a href="https://x.com/HeyPuter" class="social-link" target="_blank"><svg
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-twitter-x" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z" />
|
||||
</svg></a></li>
|
||||
<li class="no-hover"><a href="https://reddit.com/r/puter" class="social-link" target="_blank"><svg
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-reddit" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M6.167 8a.83.83 0 0 0-.83.83c0 .459.372.84.83.831a.831.831 0 0 0 0-1.661m1.843 3.647c.315 0 1.403-.038 1.976-.611a.23.23 0 0 0 0-.306.213.213 0 0 0-.306 0c-.353.363-1.126.487-1.67.487-.545 0-1.308-.124-1.671-.487a.213.213 0 0 0-.306 0 .213.213 0 0 0 0 .306c.564.563 1.652.61 1.977.61zm.992-2.807c0 .458.373.83.831.83s.83-.381.83-.83a.831.831 0 0 0-1.66 0z" />
|
||||
<path
|
||||
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-3.828-1.165c-.315 0-.602.124-.812.325-.801-.573-1.9-.945-3.121-.993l.534-2.501 1.738.372a.83.83 0 1 0 .83-.869.83.83 0 0 0-.744.468l-1.938-.41a.2.2 0 0 0-.153.028.2.2 0 0 0-.086.134l-.592 2.788c-1.24.038-2.358.41-3.17.992-.21-.2-.496-.324-.81-.324a1.163 1.163 0 0 0-.478 2.224q-.03.17-.029.353c0 1.795 2.091 3.256 4.669 3.256s4.668-1.451 4.668-3.256c0-.114-.01-.238-.029-.353.401-.181.688-.592.688-1.069 0-.65-.525-1.165-1.165-1.165" />
|
||||
</svg></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="main">
|
||||
<!---------------------------------------->
|
||||
<!-- Earn money -->
|
||||
<!---------------------------------------->
|
||||
<dialog id="earn-money">
|
||||
<h3 style="font-size: 30px; margin-top:10px; font-weight: 500;">Developers earn money on Puter!</h3>
|
||||
<p>Follow the steps below to start earning money on Puter:</p>
|
||||
<ol>
|
||||
<li>Publish as many apps as you want on Puter.</li>
|
||||
<li>We automatically review every app continuously. Qualified apps are automatically added to our Incentive Program to earn money.</li>
|
||||
<li>You will earn money every time your approved apps are opened by users.</li>
|
||||
</ol>
|
||||
<span class="close-message" id="earn-money-c2a-close" data-target="#earn-money">✕</span>
|
||||
<hr>
|
||||
<span style="font-size:15px;">Questions? Contact us: <a href="mailto:hi@puter.com" style="outline: none;">hi@puter.com</a></span>
|
||||
<a style="font-size: 14px; float: right; outline: none;" href="https://puter.com/incentive-program-terms" target="_blank">Incentive Program Terms</a>
|
||||
</dialog>
|
||||
<!---------------------------------------->
|
||||
<!-- Earn money -->
|
||||
<!---------------------------------------->
|
||||
<dialog id="earn-money">
|
||||
<h3 style="font-size: 30px; margin-top:10px; font-weight: 500;">Developers earn money on Puter!</h3>
|
||||
<p>Follow the steps below to start earning money on Puter:</p>
|
||||
<ol>
|
||||
<li>Publish as many apps as you want on Puter.</li>
|
||||
<li>We automatically review every app continuously. Qualified apps are automatically added to our
|
||||
Incentive Program to earn money.</li>
|
||||
<li>You will earn money every time your approved apps are opened by users.</li>
|
||||
</ol>
|
||||
<span class="close-message" id="earn-money-c2a-close" data-target="#earn-money">✕</span>
|
||||
<hr>
|
||||
<span style="font-size:15px;">Questions? Contact us: <a href="mailto:hi@puter.com"
|
||||
style="outline: none;">hi@puter.com</a></span>
|
||||
<a style="font-size: 14px; float: right; outline: none;" href="https://puter.com/incentive-program-terms"
|
||||
target="_blank">Incentive Program Terms</a>
|
||||
</dialog>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- Dev Incentive Program -->
|
||||
<!---------------------------------------->
|
||||
<section id="join-incentive-program">
|
||||
<section id="jip-form">
|
||||
<h1 style="font-weight: 400;">Great News!</h1>
|
||||
<p>You are approved to join the Puter Incentive Program: A revolutionary, invite-only program to earn money every time your apps are opened!</p>
|
||||
<p>Please use the following form to join the program.</p>
|
||||
<form style="clear:both;">
|
||||
<div>
|
||||
<div class="error" id="jip-error" style="width: 390px;"></div>
|
||||
|
||||
<div style="margin-bottom: 10px; overflow:hidden;">
|
||||
<div style="width: 200px; float:left;">
|
||||
<label for="jip-first-name">First Name</label>
|
||||
<input type="text" id="jip-first-name" placeholder="">
|
||||
<!---------------------------------------->
|
||||
<!-- Dev Incentive Program -->
|
||||
<!---------------------------------------->
|
||||
<section id="join-incentive-program">
|
||||
<section id="jip-form">
|
||||
<h1 style="font-weight: 400;">Great News!</h1>
|
||||
<p>You are approved to join the Puter Incentive Program: A revolutionary, invite-only program to earn
|
||||
money every time your apps are opened!</p>
|
||||
<p>Please use the following form to join the program.</p>
|
||||
<form style="clear:both;">
|
||||
<div>
|
||||
<div class="error" id="jip-error" style="width: 390px;"></div>
|
||||
|
||||
<div style="margin-bottom: 10px; overflow:hidden;">
|
||||
<div style="width: 200px; float:left;">
|
||||
<label for="jip-first-name">First Name</label>
|
||||
<input type="text" id="jip-first-name" placeholder="">
|
||||
</div>
|
||||
|
||||
<div style="width: 200px; float:left; margin-left:10px;">
|
||||
<label for="jip-last-name">Last Name</label>
|
||||
<input type="text" id="jip-last-name" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="width: 200px; float:left; margin-left:10px;">
|
||||
<label for="jip-last-name">Last Name</label>
|
||||
<input type="text" id="jip-last-name" placeholder="">
|
||||
|
||||
<div style="clear: both; margin-top: 20px; width: 410px;">
|
||||
<label for="jip-paypal">Paypal email address for receiving your payouts</label>
|
||||
<input type="text" id="jip-paypal">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="clear: both; margin-top: 20px; width: 410px;">
|
||||
<label for="jip-paypal">Paypal email address for receiving your payouts</label>
|
||||
<input type="text" id="jip-paypal">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="ip-terms-notice">By clicking Join Now, you agree to our <a href="https://puter.com/incentive-program-terms" target="_blank">Incentive Program Terms</a>.</p>
|
||||
<button type="button" class="jip-submit-btn button button-large button-primary">Join Now</button>
|
||||
</form>
|
||||
<p class="ip-terms-notice">By clicking Join Now, you agree to our <a
|
||||
href="https://puter.com/incentive-program-terms" target="_blank">Incentive Program
|
||||
Terms</a>.</p>
|
||||
<button type="button" class="jip-submit-btn button button-large button-primary">Join Now</button>
|
||||
</form>
|
||||
</section>
|
||||
<section id="jip-success">
|
||||
<h1>🎉 Congratulations!</h1>
|
||||
<p>You have successfully joined the Puter Incentive Program. You will start earning money from your
|
||||
eligible apps.</p>
|
||||
<p>Please do not hesitate to contact us at <a href="mailto:hey@puter.com">hey@puter.com</a> should you
|
||||
have any questions.</p>
|
||||
<span class="close-message" data-target="#join-incentive-program">✕</span>
|
||||
</section>
|
||||
</section>
|
||||
<section id="jip-success">
|
||||
<h1>🎉 Congratulations!</h1>
|
||||
<p>You have successfully joined the Puter Incentive Program. You will start earning money from your eligible apps.</p>
|
||||
<p>Please do not hesitate to contact us at <a href="mailto:hey@puter.com">hey@puter.com</a> should you have any questions.</p>
|
||||
<span class="close-message" data-target="#join-incentive-program">✕</span>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- Payout Method -->
|
||||
<!---------------------------------------->
|
||||
<section id="tab-payout-method" style="display:none;">
|
||||
<h1>Payout Method</h1>
|
||||
<div style="overflow: hidden;">
|
||||
<img src="./img/paypal.svg" style="float:left; width: 40px; height: 50px;"><span
|
||||
id="payout-method-email"></span>
|
||||
</div>
|
||||
<p style="font-size:14px; margin-top:20px;"><strong>Please note:</strong> every month, you will receive your
|
||||
earnings from the previous month. The payment is usually processed within the first seven business days
|
||||
of the new month. Please do not hesitate to contact us at <a
|
||||
href="mailto:hey@puter.com">hey@puter.com</a> should you have any questions.</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- Payout Method -->
|
||||
<!---------------------------------------->
|
||||
<section id="tab-payout-method" style="display:none;">
|
||||
<h1>Payout Method</h1>
|
||||
<div style="overflow: hidden;">
|
||||
<img src="./img/paypal.svg" style="float:left; width: 40px; height: 50px;"><span id="payout-method-email"></span>
|
||||
</div>
|
||||
<p style="font-size:14px; margin-top:20px;"><strong>Please note:</strong> every month, you will receive your earnings from the previous month. The payment is usually processed within the first seven business days of the new month. Please do not hesitate to contact us at <a href="mailto:hey@puter.com">hey@puter.com</a> should you have any questions.</p>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- No Apps Messaage -->
|
||||
<!---------------------------------------->
|
||||
<section id="no-apps-notice" style="display:none;">
|
||||
<img src="./img/apps-black.svg" style="width: 64px; opacity: 0.12;">
|
||||
<p style="color: #606062;">You haven't created any apps yet.</p>
|
||||
<button class="create-an-app-btn button button-primary"><img src="./img/plus.svg">Create an App</button>
|
||||
</section>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- No Apps Messaage -->
|
||||
<!---------------------------------------->
|
||||
<section id="no-apps-notice" style="display:none;">
|
||||
<img src="./img/apps-black.svg" style="width: 64px; opacity: 0.12;">
|
||||
<p style="color: #606062;">You haven't created any apps yet.</p>
|
||||
<button class="create-an-app-btn button button-primary"><img src="./img/plus.svg">Create an App</button>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- No Workers Message -->
|
||||
<!---------------------------------------->
|
||||
<section id="no-workers-notice" style="display:none;">
|
||||
<img src="./img/workers-placeholder.svg"
|
||||
style="width: 64px; height: 64px; opacity: 0.62; filter: grayscale(100%); transform: rotate(-20deg);">
|
||||
<p style="color: #606062;">You haven't created any workers yet.</p>
|
||||
<button class="create-a-worker-btn button button-primary"><img src="./img/plus.svg">Create a Worker</button>
|
||||
</section>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- No Workers Message -->
|
||||
<!---------------------------------------->
|
||||
<section id="no-workers-notice" style="display:none;">
|
||||
<img src="./img/workers-placeholder.svg" style="width: 64px; height: 64px; opacity: 0.62; filter: grayscale(100%); transform: rotate(-20deg);">
|
||||
<p style="color: #606062;">You haven't created any workers yet.</p>
|
||||
<button class="create-a-worker-btn button button-primary"><img src="./img/plus.svg">Create a Worker</button>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- No Websites Message -->
|
||||
<!---------------------------------------->
|
||||
<section id="no-websites-notice" style="display:none;">
|
||||
<img src="./img/websites-placeholder.svg"
|
||||
style="width: 64px; height: 64px; opacity: 0.22; filter: grayscale(100%);">
|
||||
<p style="color: #606062;">You haven't created any websites yet.</p>
|
||||
<button class="create-a-website-btn button button-primary"><img src="./img/plus.svg">Create a
|
||||
Website</button>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- Edit App -->
|
||||
<!---------------------------------------->
|
||||
<section id="edit-app" style="margin-bottom: 100px;">
|
||||
</section>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- No Websites Message -->
|
||||
<!---------------------------------------->
|
||||
<section id="no-websites-notice" style="display:none;">
|
||||
<img src="./img/websites-placeholder.svg" style="width: 64px; height: 64px; opacity: 0.22; filter: grayscale(100%);">
|
||||
<p style="color: #606062;">You haven't created any websites yet.</p>
|
||||
<button class="create-a-website-btn button button-primary"><img src="./img/plus.svg">Create a Website</button>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- Edit App -->
|
||||
<!---------------------------------------->
|
||||
<section id="edit-app" style="margin-bottom: 100px;">
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- Insta-Deploy Modal -->
|
||||
<!---------------------------------------->
|
||||
<dialog class="insta-deploy-modal">
|
||||
<p>Deploy <strong class="insta-deploy-item-name"></strong> to:</p>
|
||||
<div style="overflow: hidden;">
|
||||
<div class="insta-deploy-to-new-app">New App</div>
|
||||
<div class="insta-deploy-to-existing-app">An Existing App</div>
|
||||
</div>
|
||||
<span class="insta-deploy-cancel">Cancel</span>
|
||||
</dialog>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- Insta-Deploy Modal -->
|
||||
<!---------------------------------------->
|
||||
<dialog class="insta-deploy-modal">
|
||||
<p>Deploy <strong class="insta-deploy-item-name"></strong> to:</p>
|
||||
<div style="overflow: hidden;">
|
||||
<div class="insta-deploy-to-new-app">New App</div>
|
||||
<div class="insta-deploy-to-existing-app">An Existing App</div>
|
||||
</div>
|
||||
<span class="insta-deploy-cancel">Cancel</span>
|
||||
</dialog>
|
||||
<dialog class="insta-deploy-existing-app-select">
|
||||
<span class="insta-deploy-existing-app-back">Back</span>
|
||||
<p>Select app to deploy to:</p>
|
||||
<div class="insta-deploy-existing-app-list"></div>
|
||||
<button style="margin-top: 10px;"
|
||||
class="button button-primary button-block disabled insta-deploy-existing-app-deploy-btn">Deploy</button>
|
||||
<div class="insta-deploy-cancel">Cancel</div>
|
||||
</dialog>
|
||||
|
||||
<dialog class="insta-deploy-existing-app-select">
|
||||
<span class="insta-deploy-existing-app-back">Back</span>
|
||||
<p>Select app to deploy to:</p>
|
||||
<div class="insta-deploy-existing-app-list"></div>
|
||||
<button style="margin-top: 10px;" class="button button-primary button-block disabled insta-deploy-existing-app-deploy-btn">Deploy</button>
|
||||
<div class="insta-deploy-cancel">Cancel</div>
|
||||
</dialog>
|
||||
<!---------------------------------------->
|
||||
<!-- App List -->
|
||||
<!---------------------------------------->
|
||||
<section id="app-list">
|
||||
<div class="app-list-nav">
|
||||
<h1 class="my-apps-title">My Apps<span class="app-count"></span></h1>
|
||||
<button class="setup-account-btn button button-secondary" style="float:right; margin-bottom: 10px;">Open
|
||||
Payments Dev Account</button>
|
||||
<button class="create-an-app-btn button button-primary" style="float:right; margin-bottom: 10px;">New
|
||||
App</button>
|
||||
</div>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- App List -->
|
||||
<!---------------------------------------->
|
||||
<section id="app-list">
|
||||
<div class="app-list-nav">
|
||||
<h1 class="my-apps-title">My Apps<span class="app-count"></span></h1>
|
||||
<button class="create-an-app-btn button button-primary"><img src="./img/plus.svg" style="width: 25px; height: 25px;">New App</button>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<input style="background-image:url(./img/magnifier-outline.svg);" class="search search-apps"
|
||||
placeholder="Search apps">
|
||||
<img class="search-clear search-clear-apps" src="./img/close.svg">
|
||||
</div>
|
||||
|
||||
<div class="search-container">
|
||||
<input style="background-image:url(./img/magnifier-outline.svg);" class="search search-apps" placeholder="Search apps">
|
||||
<img class="search-clear search-clear-apps" src="./img/close.svg">
|
||||
</div>
|
||||
<button class="button button-danger disabled delete-apps-btn" style="float:right;">Delete</button>
|
||||
<button class="refresh-app-list" title="Refresh"><svg class="refresh-icon"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="32px" height="32px" viewBox="0 0 32 32" stroke-width="2">
|
||||
<g stroke-width="2" transform="translate(0.5, 0.5)">
|
||||
<path data-cap="butt" d="M29.382,9.217A15,15,0,0,0,1,16" fill="none" stroke="#444444"
|
||||
stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</path>
|
||||
<polyline points="28.383 1.22 29.383 9.22 21.383 8.22" fill="none" stroke="#444444"
|
||||
stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter">
|
||||
</polyline>
|
||||
<path data-cap="butt" data-color="color-2" d="M2.618,22.783A15,15,0,0,0,31,16" fill="none"
|
||||
stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt"
|
||||
stroke-linejoin="miter"></path>
|
||||
<polyline data-color="color-2" points="3.617 30.78 2.617 22.78 10.617 23.78" fill="none"
|
||||
stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"
|
||||
stroke-linejoin="miter"></polyline>
|
||||
</g>
|
||||
</svg></button>
|
||||
<div style="overflow-x: auto; clear: both;">
|
||||
<table class="table" id="app-list-table">
|
||||
<thead class="disable-user-select">
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-apps"
|
||||
style="width: 15px; height: 20px; margin-left:3px;"></th>
|
||||
<th class="sort th-name" data-column="name" style="padding-left: 10px !important;">App<span
|
||||
class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-users" data-column="user_count">Users<span
|
||||
class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-opens" data-column="open_count">Opens<span
|
||||
class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-created sorted" data-column="created_at">Created<span
|
||||
class="sort-arrow sort-arrow-desc" style="display:inline;">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<button class="button button-danger disabled delete-apps-btn" style="float:right;">Delete</button>
|
||||
<button class="refresh-app-list" title="Refresh"><svg class="refresh-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32" stroke-width="2"><g stroke-width="2" transform="translate(0.5, 0.5)"><path data-cap="butt" d="M29.382,9.217A15,15,0,0,0,1,16" fill="none" stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline points="28.383 1.22 29.383 9.22 21.383 8.22" fill="none" stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline><path data-cap="butt" data-color="color-2" d="M2.618,22.783A15,15,0,0,0,31,16" fill="none" stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline data-color="color-2" points="3.617 30.78 2.617 22.78 10.617 23.78" fill="none" stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline></g></svg></button>
|
||||
<div style="overflow-x: auto; clear: both;">
|
||||
<table class="table" id="app-list-table">
|
||||
<thead class="disable-user-select">
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-apps" style="width: 15px; height: 20px; margin-left:3px;"></th>
|
||||
<th class="sort th-name" data-column="name" style="padding-left: 10px !important;">App<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-users" data-column="user_count">Users<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-opens" data-column="open_count">Opens<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-created sorted" data-column="created_at">Created<span class="sort-arrow sort-arrow-desc" style="display:inline;">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- Worker List -->
|
||||
<!---------------------------------------->
|
||||
<section id="worker-list">
|
||||
<div class="worker-list-nav">
|
||||
<h1 class="my-workers-title">My Workers<span class="worker-count"></span></h1>
|
||||
<button class="create-a-worker-btn button button-primary"><img src="./img/plus.svg"
|
||||
style="width: 25px; height: 25px;"> New Worker</button>
|
||||
</div>
|
||||
|
||||
<!---------------------------------------->
|
||||
<!-- Worker List -->
|
||||
<!---------------------------------------->
|
||||
<section id="worker-list">
|
||||
<div class="worker-list-nav">
|
||||
<h1 class="my-workers-title">My Workers<span class="worker-count"></span></h1>
|
||||
<button class="create-a-worker-btn button button-primary"><img src="./img/plus.svg" style="width: 25px; height: 25px;"> New Worker</button>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<input style="background-image:url(./img/magnifier-outline.svg);" class="search search-workers"
|
||||
placeholder="Search workers">
|
||||
<img class="search-clear search-clear-workers" src="./img/close.svg">
|
||||
</div>
|
||||
|
||||
<div class="search-container">
|
||||
<input style="background-image:url(./img/magnifier-outline.svg);" class="search search-workers" placeholder="Search workers">
|
||||
<img class="search-clear search-clear-workers" src="./img/close.svg">
|
||||
</div>
|
||||
<button class="button button-danger disabled delete-workers-btn" style="float:right;">Delete</button>
|
||||
<button class="refresh-worker-list" title="Refresh"><svg class="refresh-icon"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="32px" height="32px" viewBox="0 0 32 32" stroke-width="2">
|
||||
<g stroke-width="2" transform="translate(0.5, 0.5)">
|
||||
<path data-cap="butt" d="M29.382,9.217A15,15,0,0,0,1,16" fill="none" stroke="#444444"
|
||||
stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</path>
|
||||
<polyline points="28.383 1.22 29.383 9.22 21.383 8.22" fill="none" stroke="#444444"
|
||||
stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter">
|
||||
</polyline>
|
||||
<path data-cap="butt" data-color="color-2" d="M2.618,22.783A15,15,0,0,0,31,16" fill="none"
|
||||
stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt"
|
||||
stroke-linejoin="miter"></path>
|
||||
<polyline data-color="color-2" points="3.617 30.78 2.617 22.78 10.617 23.78" fill="none"
|
||||
stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"
|
||||
stroke-linejoin="miter"></polyline>
|
||||
</g>
|
||||
</svg></button>
|
||||
<div style="overflow-x: auto; clear: both;">
|
||||
<table class="table" id="worker-list-table">
|
||||
<thead class="disable-user-select">
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-workers"
|
||||
style="width: 15px; height: 20px; margin-left:3px;"></th>
|
||||
<th class="sort th-name" data-column="name" style="padding-left: 10px !important;">
|
||||
Worker<span class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-file" data-column="file_path">File<span
|
||||
class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-created sorted" data-column="created_at">Created<span
|
||||
class="sort-arrow sort-arrow-desc" style="display:inline;">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<button class="button button-danger disabled delete-workers-btn" style="float:right;">Delete</button>
|
||||
<button class="refresh-worker-list" title="Refresh"><svg class="refresh-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32" stroke-width="2"><g stroke-width="2" transform="translate(0.5, 0.5)"><path data-cap="butt" d="M29.382,9.217A15,15,0,0,0,1,16" fill="none" stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline points="28.383 1.22 29.383 9.22 21.383 8.22" fill="none" stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline><path data-cap="butt" data-color="color-2" d="M2.618,22.783A15,15,0,0,0,31,16" fill="none" stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline data-color="color-2" points="3.617 30.78 2.617 22.78 10.617 23.78" fill="none" stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline></g></svg></button>
|
||||
<div style="overflow-x: auto; clear: both;">
|
||||
<table class="table" id="worker-list-table">
|
||||
<thead class="disable-user-select">
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-workers" style="width: 15px; height: 20px; margin-left:3px;"></th>
|
||||
<th class="sort th-name" data-column="name" style="padding-left: 10px !important;">Worker<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-file" data-column="file_path">File<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-created sorted" data-column="created_at">Created<span class="sort-arrow sort-arrow-desc" style="display:inline;">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- Websites List -->
|
||||
<!---------------------------------------->
|
||||
<section id="website-list">
|
||||
<div class="website-list-nav">
|
||||
<h1 class="my-websites-title">My Websites<span class="website-count"></span></h1>
|
||||
<button class="create-a-website-btn button button-primary"><img src="./img/plus.svg"
|
||||
style="width: 25px; height: 25px;">New Website</button>
|
||||
</div>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<!---------------------------------------->
|
||||
<!-- Websites List -->
|
||||
<!---------------------------------------->
|
||||
<section id="website-list">
|
||||
<div class="website-list-nav">
|
||||
<h1 class="my-websites-title">My Websites<span class="website-count"></span></h1>
|
||||
<button class="create-a-website-btn button button-primary"><img src="./img/plus.svg" style="width: 25px; height: 25px;">New Website</button>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<input style="background-image:url(./img/magnifier-outline.svg);" class="search search-websites"
|
||||
placeholder="Search websites">
|
||||
<img class="search-clear search-clear-websites" src="./img/close.svg">
|
||||
</div>
|
||||
|
||||
<div class="search-container">
|
||||
<input style="background-image:url(./img/magnifier-outline.svg);" class="search search-websites" placeholder="Search websites">
|
||||
<img class="search-clear search-clear-websites" src="./img/close.svg">
|
||||
</div>
|
||||
<button class="button button-danger disabled delete-websites-btn" style="float:right;">Delete</button>
|
||||
<button class="refresh-website-list" style="width:40px; padding: 10px; float: right; margin-right: 10px;"
|
||||
title="Refresh"><svg class="refresh-icon" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px"
|
||||
viewBox="0 0 32 32" stroke-width="2">
|
||||
<g stroke-width="2" transform="translate(0.5, 0.5)">
|
||||
<path data-cap="butt" d="M29.382,9.217A15,15,0,0,0,1,16" fill="none" stroke="#444444"
|
||||
stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter">
|
||||
</path>
|
||||
<polyline points="28.383 1.22 29.383 9.22 21.383 8.22" fill="none" stroke="#444444"
|
||||
stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter">
|
||||
</polyline>
|
||||
<path data-cap="butt" data-color="color-2" d="M2.618,22.783A15,15,0,0,0,31,16" fill="none"
|
||||
stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt"
|
||||
stroke-linejoin="miter"></path>
|
||||
<polyline data-color="color-2" points="3.617 30.78 2.617 22.78 10.617 23.78" fill="none"
|
||||
stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2"
|
||||
stroke-linejoin="miter"></polyline>
|
||||
</g>
|
||||
</svg></button>
|
||||
<div style="overflow-x: auto; clear: both;">
|
||||
<table class="table" id="website-list-table">
|
||||
<thead class="disable-user-select">
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-websites"
|
||||
style="width: 15px; height: 20px; margin-left:3px;"></th>
|
||||
<th class="sort th-name" data-column="name" style="padding-left: 10px !important;">
|
||||
Website<span class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-root-dir" data-column="root_dir">Connected Directory<span
|
||||
class="sort-arrow sort-arrow-desc">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-created sorted" data-column="created_at">Created<span
|
||||
class="sort-arrow sort-arrow-desc" style="display:inline;">▼</span><span
|
||||
class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<button class="button button-danger disabled delete-websites-btn" style="float:right;">Delete</button>
|
||||
<button class="refresh-website-list" style="width:40px; padding: 10px; float: right; margin-right: 10px;" title="Refresh"><svg class="refresh-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32" stroke-width="2"><g stroke-width="2" transform="translate(0.5, 0.5)"><path data-cap="butt" d="M29.382,9.217A15,15,0,0,0,1,16" fill="none" stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline points="28.383 1.22 29.383 9.22 21.383 8.22" fill="none" stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline><path data-cap="butt" data-color="color-2" d="M2.618,22.783A15,15,0,0,0,31,16" fill="none" stroke="#444444" stroke-miterlimit="10" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter"></path><polyline data-color="color-2" points="3.617 30.78 2.617 22.78 10.617 23.78" fill="none" stroke="#444444" stroke-linecap="square" stroke-miterlimit="10" stroke-width="2" stroke-linejoin="miter"></polyline></g></svg></button>
|
||||
<div style="overflow-x: auto; clear: both;">
|
||||
<table class="table" id="website-list-table">
|
||||
<thead class="disable-user-select">
|
||||
<tr>
|
||||
<th><input type="checkbox" class="select-all-websites" style="width: 15px; height: 20px; margin-left:3px;"></th>
|
||||
<th class="sort th-name" data-column="name" style="padding-left: 10px !important;">Website<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-root-dir" data-column="root_dir">Connected Directory<span class="sort-arrow sort-arrow-desc">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th class="sort th-created sorted" data-column="created_at">Created<span class="sort-arrow sort-arrow-desc" style="display:inline;">▼</span><span class="sort-arrow sort-arrow-asc">▲</span></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="https://js.puter.com/v2/"></script>
|
||||
|
||||
+794
-739
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user