From d86ece5bd4ac21cc25c59bd2c76f05216914d859 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Thu, 2 Oct 2025 19:16:51 -0500 Subject: [PATCH 01/79] wip adding a gui for joystickdb --- node/dist/app/index.js | 2 +- node/dist/lib/joystickdb_gui/api/getters.js | 1 + node/dist/lib/joystickdb_gui/api/index.js | 1 + node/dist/lib/joystickdb_gui/api/setters.js | 1 + .../client/joystickdb_client.js | 1 + node/dist/lib/joystickdb_gui/index.js | 1 + .../lib/joystickdb_gui/middleware/auth.js | 1 + node/dist/lib/joystickdb_gui/routes/index.js | 1 + .../lib/joystickdb_gui/ui/layouts/main.js | 331 +++++++++++++++++ .../lib/joystickdb_gui/ui/pages/dashboard.js | 81 +++++ .../dist/lib/joystickdb_gui/ui/pages/login.js | 102 ++++++ node/src/app/index.js | 80 +++++ node/src/lib/joystickdb_gui/api/getters.js | 183 ++++++++++ node/src/lib/joystickdb_gui/api/index.js | 13 + node/src/lib/joystickdb_gui/api/setters.js | 226 ++++++++++++ .../client/joystickdb_client.js | 45 +++ node/src/lib/joystickdb_gui/index.js | 9 + .../src/lib/joystickdb_gui/middleware/auth.js | 10 + node/src/lib/joystickdb_gui/routes/index.js | 117 ++++++ .../src/lib/joystickdb_gui/ui/layouts/main.js | 340 ++++++++++++++++++ .../lib/joystickdb_gui/ui/pages/dashboard.js | 96 +++++ node/src/lib/joystickdb_gui/ui/pages/login.js | 135 +++++++ 22 files changed, 1776 insertions(+), 1 deletion(-) create mode 100644 node/dist/lib/joystickdb_gui/api/getters.js create mode 100644 node/dist/lib/joystickdb_gui/api/index.js create mode 100644 node/dist/lib/joystickdb_gui/api/setters.js create mode 100644 node/dist/lib/joystickdb_gui/client/joystickdb_client.js create mode 100644 node/dist/lib/joystickdb_gui/index.js create mode 100644 node/dist/lib/joystickdb_gui/middleware/auth.js create mode 100644 node/dist/lib/joystickdb_gui/routes/index.js create mode 100644 node/dist/lib/joystickdb_gui/ui/layouts/main.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/dashboard.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/login.js create mode 100644 node/src/lib/joystickdb_gui/api/getters.js create mode 100644 node/src/lib/joystickdb_gui/api/index.js create mode 100644 node/src/lib/joystickdb_gui/api/setters.js create mode 100644 node/src/lib/joystickdb_gui/client/joystickdb_client.js create mode 100644 node/src/lib/joystickdb_gui/index.js create mode 100644 node/src/lib/joystickdb_gui/middleware/auth.js create mode 100644 node/src/lib/joystickdb_gui/routes/index.js create mode 100644 node/src/lib/joystickdb_gui/ui/layouts/main.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/dashboard.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/login.js diff --git a/node/dist/app/index.js b/node/dist/app/index.js index 702cf23f1..bbac898ce 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import v from"http";import $ from"fs";import q from"./api/accounts/authenticated.js";import O from"./api/accounts/login.js";import E from"./api/accounts/logout.js";import F from"./api/accounts/recover_password.js";import N from"./api/accounts/reset_password.js";import R from"./api/accounts/signup.js";import S from"./api/accounts/user.js";import D from"./api/accounts/verify_email.js";import I from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import A from"./api/test/bootstrap.js";import T from"./api/test/process.js";import P from"./api/test/queues.js";import U from"./databases/mongodb/create_indexes.js";import C from"./databases/postgresql/create_indexes.js";import J from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import L from"./generate_machine_id.js";import B from"./generate_process_id.js";import H from"../lib/get_browser_safe_request.js";import d from"../lib/get_joystick_build_path.js";import b from"./databases/get_target_database_connection.js";import V from"../lib/get_translations.js";import Y from"./handle_process_errors.js";import z from"./settings/load.js";import K from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Q from"./push/index.js";import G from"./push/logger.js";import M from"./queues/index.js";import f from"../lib/read_mod_component_css.js";import j from"../lib/read_mod_global_css.js";import X from"./register_app_options.js";import Z from"./cron_jobs/register.js";import tt from"./databases/register_database.js";import st from"./api/register_getters.js";import et from"./routes/register_route_from_function.js";import ot from"./routes/register_route_from_object.js";import it from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import pt from"./start_express.js";import ct from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:g}=$.promises,lt=z();class dt{constructor(t={}){v.globalAgent.maxSockets=1/0,Y(t?.events),X(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases;for(let s=0;si?.provider===o?.provider)?.length>1;await tt(i,e,r)}if(t?.length>0){const s=b("queues"),i=b("users");process.databases._queues=s?.connection,process.databases._users=i?.connection;const e=[s,i],r=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await U(r);const o=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await J(o),await C(o)}}async generate_machine_id(){this.joystick_machine_id=await L()}async generate_process_id(){this.joystick_process_id=await B()}async load_translations(){const t=d();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async i=>{const e=process._joystick_translations[i];if(await p(e.path))try{const r=await g(e.path);e.files=r.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e.files){const a=`${e.path}/${o}`;try{const n=await h(a);e.cache[o]=n}catch(n){console.warn(`Failed to load translation file: ${a}`,n.message)}}}catch(r){console.warn(`Failed to scan translation directory: ${e.path}`,r.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${d()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await g(s),e=i.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e){const a=`${s}/${o}`,n=o.replace(".js","");try{const c=await h(a);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const r=i.filter(o=>o.startsWith("base")&&(o.endsWith(".html")||o.endsWith(".css")));for(const o of r){const a=`${s}/${o}`;try{const n=await _(a,"utf-8");process._joystick_email_base_files[o]=n}catch(n){console.warn(`Failed to load email base file: ${a}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${s}`,i.message)}}}async load_ui(){const t=d();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const r=await g(e,{withFileTypes:!0});for(const o of r)if(o.isDirectory()){const n=`${`${e}/${o.name}`}/index.js`;if(await p(n))try{const c=await h(n),u=n.replace(`${t}`,"");process._joystick_components[u]=c}catch(c){console.warn(`Failed to load component: ${n}`,c.message)}}}catch(r){console.warn(`Failed to scan directory: ${e}`,r.message)}},i=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of i)await p(e)&&await s(e)}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const i=JSON.parse(s);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",q),this.express.app.post("/api/_accounts/user",S),this.express.app.post("/api/_accounts/login",O),this.express.app.post("/api/_accounts/logout",E),this.express.app.post("/api/_accounts/recover-password",F),this.express.app.post("/api/_accounts/reset-password",N),this.express.app.post("/api/_accounts/signup",R),this.express.app.get("/api/_accounts/verify-email",D)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,i=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&st(this.express.app,Object.entries(t||{}),e,i),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&it(this.express.app,Object.entries(s||{}),e,i)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){Z(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const i=d(),e=_t(t?.body?.page),r=`${i}/${e}`;if(!t?.body?.page||!await p(r))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${r}.`),s);const o=await h(r);if(o){const a=K(t?.body?.route_pattern||"",t?.body?.path),n=H({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:o,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let u={};try{const k=`${d()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(u=await V({req:n,language_files_path:k,language_files:w,render_component_path:e}))}catch(y){console.warn("Failed to load translations for dynamic page:",y.message)}return s.status(200).send({data:c,req:n,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:u})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,e,r,o={},a={};if(s==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",r={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},o=await j();const n=await f("free"),c=await f("plus");a={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",r={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},o=await j(),a={...await f("free")||{}};this.mod={version:s,css:{light:i,dark:e},js:r,globals:o,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Q(),await G(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sA(t,s,this)),this.express.app.get("/api/_test/process",T),this.express.app.delete("/api/_test/accounts",I),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>P(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=pt(this.on_after_start_server,this)}}const x=async(l={})=>{const t=new dt(l);return await t.start(l),t},ut=(l={})=>new Promise(async t=>{if(l?.cluster)ct(async()=>{const s=await x(l);return t(s.express)});else{const s=await x(l);return t(s.express)}});var _s=ut;export{_s as default}; +import F from"http";import D from"fs";import E from"./api/accounts/authenticated.js";import I from"./api/accounts/login.js";import N from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import W from"./api/accounts/reset_password.js";import S from"./api/accounts/signup.js";import J from"./api/accounts/user.js";import T from"./api/accounts/verify_email.js";import A from"./api/test/accounts/delete.js";import B from"./api/test/accounts/signup.js";import U from"./api/test/bootstrap.js";import P from"./api/test/process.js";import C from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import G from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import j from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import k from"./api/register_getters.js";import x from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import v from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import l from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:m,readdir:g}=D.promises,lt=M();class dt{constructor(s={}){F.globalAgent.maxSockets=1/0,Q(s?.events),et(this,s),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:s,external_process_ids:[],track_external_process:(t="")=>{process.send({external_process_id:t}),process.joystick.external_process_ids.push(t)}}}async connect_databases(){const s=lt?.config?.databases;for(let t=0;ti?.provider===e?.provider)?.length>1;await it(i,o,a)}if(s?.length>0){const t=w("queues"),i=w("users");process.databases._queues=t?.connection,process.databases._users=i?.connection;const o=[t,i],a=o?.filter(r=>r?.provider==="mongodb")?.map(r=>r?.database_type);await L(a);const e=o?.filter(r=>r?.provider==="postgresql")?.map(r=>r?.database_type);await H(e),await G(e)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const s=f();if(!await p(s))return;process._joystick_translations={normal:{files:[],path:`${s}i18n`,cache:{}},email:{files:[],path:`${s}i18n/email`,cache:{}}};const t=async i=>{const o=process._joystick_translations[i];if(await p(o.path))try{const a=await g(o.path);o.files=a.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of o.files){const r=`${o.path}/${e}`;try{const n=await h(r);o.cache[e]=n}catch(n){console.warn(`Failed to load translation file: ${r}`,n.message)}}}catch(a){console.warn(`Failed to scan translation directory: ${o.path}`,a.message)}};await t("normal"),await t("email")}async load_email_templates(){const t=`${f()}email`;if(await p(t)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await g(t),o=i.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of o){const r=`${t}/${e}`,n=e.replace(".js","");try{const c=await h(r);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${r}`,c.message)}}const a=i.filter(e=>e.startsWith("base")&&(e.endsWith(".html")||e.endsWith(".css")));for(const e of a){const r=`${t}/${e}`;try{const n=await m(r,"utf-8");process._joystick_email_base_files[e]=n}catch(n){console.warn(`Failed to load email base file: ${r}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${t}`,i.message)}}}async load_ui(){const s=f();if(!await p(s))return;process._joystick_html=await m("index.html","utf-8"),process._joystick_components={};const t=async a=>{try{const e=await g(a,{withFileTypes:!0});for(const r of e)if(r.isDirectory()){const c=`${`${a}/${r.name}`}/index.js`;if(await p(c))try{const _=await h(c),u=c.replace(`${s}`,"");process._joystick_components[u]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(e){console.warn(`Failed to scan directory: ${a}`,e.message)}},i=[`${s}ui/components`,`${s}ui/layouts`,`${s}ui/pages`];for(const a of i)await p(a)&&await t(a);const o=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(o)){const a=async e=>{try{const r=await g(e,{withFileTypes:!0});for(const n of r)if(n.isDirectory())await a(`${e}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${e}/${n.name}`;try{const _=await h(c),u=c.replace(`${process.cwd()}/`,"");process._joystick_components[u]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(r){console.warn(`Failed to scan JoystickDB directory: ${e}`,r.message)}};await a(`${o}/layouts`),await a(`${o}/pages`)}}on_after_start_server(s={}){process.on("message",t=>{if(typeof t=="string"){const i=JSON.parse(t);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(t))}}),console.log(`App running at: http://localhost:${s.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",J),this.express.app.post("/api/_accounts/login",I),this.express.app.post("/api/_accounts/logout",N),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",W),this.express.app.post("/api/_accounts/signup",S),this.express.app.get("/api/_accounts/verify-email",T)}register_api(){const s=this?.options?.api?.getters,t=this?.options?.api?.setters,i=this?.options?.api?.options,o=this?.options?.api?.context;s&&l.is_object(s)&&Object.keys(s||{}).length>0&&k(this.express.app,Object.entries(s||{}),o,i),t&&l.is_object(t)&&Object.keys(t||{}).length>0&&v(this.express.app,Object.entries(t||{}),o,i)}register_caches(){process.caches={},l.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(s={},t={})=>{const i=f(),o=_t(s?.body?.page),a=`${i}/${o}`;if(!s?.body?.page||!await p(a))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${a}.`),t);const e=await h(a);if(e){const r=X(s?.body?.route_pattern||"",s?.body?.path),n=z({params:r?.params||{},query:s?.body?.query_params||{},url:s?.body?.path,headers:s?.headers,context:s?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:e,api_schema:this?.options?.api,component_options:{props:s?.body?.props},req:n});let _={};try{const q=`${f()}i18n`,b=process._joystick_translations?.normal?.files||[];b.length>0&&(_=await K({req:n,language_files_path:q,language_files:b,render_component_path:o}))}catch(u){console.warn("Failed to load translations for dynamic page:",u.message)}return t.status(200).send({data:c,req:n,url:{params:r?.params||{},query:s?.body?.query_params||{},path:s?.body?.path,route:s?.body?.route_pattern||s?.body?.path},i18n:_})}return t.status(200).send({})})}register_fixtures(){l.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){l.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const t=await p("private/mod/mod_version.txt")&&(await m("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,o,a,e={},r={};if(t==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await m("private/mod/mod-light-plus.min.css","utf-8")||"",o=await p("private/mod/mod-dark-plus.min.css")&&await m("private/mod/mod-dark-plus.min.css","utf-8")||"",a={esm:await p("lib/mod-plus.esm.min.js")&&await m("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await m("lib/mod-plus.iife.min.js","utf-8")||""},e=await j();const n=await y("free"),c=await y("plus");r={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await m("private/mod/mod-light.min.css","utf-8")||"",o=await p("private/mod/mod-dark.min.css")&&await m("private/mod/mod-dark.min.css","utf-8")||"",a={esm:await p("lib/mod.esm.min.js")&&await m("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await m("lib/mod.iife.min.js","utf-8")||""},e=await j(),r={...await y("free")||{}};this.mod={version:t,css:{light:i,dark:o},js:a,globals:e,components:r}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(s={},t={})=>s?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?t.status(403).send("403 - You are not allowed to access this endpoint."):t.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(l.is_object(this.options.queues)){const s=Object.entries(this.options.queues||{});for(let t=0;tU(s,t,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",A),this.express.app.post("/api/_test/accounts/signup",B),this.express.app.post("/api/_test/queues",(s={},t={})=>C(s,t,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){if(process.databases&&Object.keys(process.databases).some(t=>t==="joystickdb"||t.startsWith("joystickdb_")))try{const t=await h("../lib/joystickdb_gui/index.js"),i=Object.entries(t.routes||{});for(let e=0;e0&&k(this.express.app,Object.entries(o),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&v(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered automatically at /joystickdb")}catch(t){console.warn("Failed to register JoystickDB GUI:",t.message)}}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),await this.register_joystickdb_gui(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const O=async(d={})=>{const s=new dt(d);return await s.start(d),s},ut=(d={})=>new Promise(async s=>{if(d?.cluster)pt(async()=>{const t=await O(d);return s(t.express)});else{const t=await O(d);return s(t.express)}});var _s=ut;export{_s as default}; diff --git a/node/dist/lib/joystickdb_gui/api/getters.js b/node/dist/lib/joystickdb_gui/api/getters.js new file mode 100644 index 000000000..5bd82b770 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/api/getters.js @@ -0,0 +1 @@ +import"../client/joystickdb_client.js";const d={joystickdb_stats:{authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");return await i.get_stats()}},joystickdb_databases:{authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");return await i.list_databases()}},joystickdb_collections:{input:{database:{type:"string",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");return await i.db(e.database).list_collections()}},joystickdb_documents:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},limit:{type:"integer",required:!1,max:1e3},skip:{type:"integer",required:!1},filter:{type:"object",required:!1},sort:{type:"object",required:!1}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");const o=i.db(e.database).collection(e.collection),r={limit:e.limit||20};e.skip&&(r.skip=e.skip),e.sort&&(r.sort=e.sort);const n=e.filter||{},c=await o.find(n,r),a=await o.count_documents(n);return{documents:c,total:a}}},joystickdb_indexes:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");return await i.db(e.database).collection(e.collection).get_indexes()}},joystickdb_replication_status:{authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");try{return await i.admin_operation({operation:"get_sync_status"})}catch(s){return{error:s.message}}}},joystickdb_document_by_id:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},document_id:{type:"string",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");return await i.db(e.database).get_document(e.collection,e.document_id)}},joystickdb_backup_list:{authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,get:async(e={},t={})=>{const i=t?.req?.session?.joystickdb_client;if(!i)throw new Error("No JoystickDB connection available");return await i.list_backups()}}};var b=d;export{b as default}; diff --git a/node/dist/lib/joystickdb_gui/api/index.js b/node/dist/lib/joystickdb_gui/api/index.js new file mode 100644 index 000000000..7f9c0769a --- /dev/null +++ b/node/dist/lib/joystickdb_gui/api/index.js @@ -0,0 +1 @@ +import t from"./getters.js";import e from"./setters.js";const s={getters:{...t},setters:{...e}};var i=s;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/api/setters.js b/node/dist/lib/joystickdb_gui/api/setters.js new file mode 100644 index 000000000..d7912ea28 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/api/setters.js @@ -0,0 +1 @@ +import n from"../client/joystickdb_client.js";const s={joystickdb_authenticate:{input:{username:{type:"string",required:!0},password:{type:"string",required:!0}},set:async(e={},t={})=>{try{const o=await new n({username:e.username,password:e.password}).connect();return t.req.session.joystickdb_user={username:e.username},t.req.session.joystickdb_client=o,{success:!0,username:e.username}}catch(r){throw new Error(`Authentication failed: ${r.message}`)}}},joystickdb_create_document:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},document:{type:"object",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.db(e.database).collection(e.collection).insert_one(e.document)}},joystickdb_update_document:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},filter:{type:"object",required:!0},update:{type:"object",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.db(e.database).collection(e.collection).update_one(e.filter,e.update)}},joystickdb_delete_document:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},filter:{type:"object",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.db(e.database).collection(e.collection).delete_one(e.filter)}},joystickdb_create_index:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},field:{type:"string",required:!0},options:{type:"object",required:!1}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.db(e.database).collection(e.collection).create_index(e.field,e.options||{})}},joystickdb_drop_index:{input:{database:{type:"string",required:!0},collection:{type:"string",required:!0},field:{type:"string",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.db(e.database).collection(e.collection).drop_index(e.field)}},joystickdb_force_sync:{authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.admin_operation({operation:"force_sync"})}},joystickdb_create_backup:{authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.backup_now()}},joystickdb_restore_backup:{input:{backup_name:{type:"string",required:!0}},authorized:(e={},t={})=>!!t?.req?.session?.joystickdb_user,set:async(e={},t={})=>{const r=t?.req?.session?.joystickdb_client;if(!r)throw new Error("No JoystickDB connection available");return await r.restore_backup(e.backup_name)}}};var a=s;export{a as default}; diff --git a/node/dist/lib/joystickdb_gui/client/joystickdb_client.js b/node/dist/lib/joystickdb_gui/client/joystickdb_client.js new file mode 100644 index 000000000..16ac8f292 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/client/joystickdb_client.js @@ -0,0 +1 @@ +import c from"../../../lib/joystickdb/index.js";class o{constructor(t){this.credentials=t,this.client=null}async connect(){if(this.client)return this.client;try{return this.client=c.client({port:1983,authentication:this.credentials,timeout:5e3}),new Promise((t,i)=>{const e=setTimeout(()=>{i(new Error("Connection timeout"))},5e3);this.client.on("authenticated",()=>{clearTimeout(e),t(this.client)}),this.client.on("error",n=>{clearTimeout(e),i(n)})})}catch(t){throw new Error(`Failed to connect to JoystickDB: ${t.message}`)}}async disconnect(){this.client&&(this.client.disconnect(),this.client=null)}}var l=o;export{l as default}; diff --git a/node/dist/lib/joystickdb_gui/index.js b/node/dist/lib/joystickdb_gui/index.js new file mode 100644 index 000000000..d1b8e8cbb --- /dev/null +++ b/node/dist/lib/joystickdb_gui/index.js @@ -0,0 +1 @@ +import o from"./routes/index.js";import t from"./api/index.js";const i={routes:o,api:t};var p=i;export{p as default}; diff --git a/node/dist/lib/joystickdb_gui/middleware/auth.js b/node/dist/lib/joystickdb_gui/middleware/auth.js new file mode 100644 index 000000000..c7580dbc0 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/middleware/auth.js @@ -0,0 +1 @@ +const o=(s={},i={},t={})=>{if(!s.session?.joystickdb_user||!s.session?.joystickdb_client)return i.redirect("/joystickdb/login?error=authentication_required");s.joystickdb_client=s.session.joystickdb_client,s.joystickdb_user=s.session.joystickdb_user,t()};var e=o;export{e as default}; diff --git a/node/dist/lib/joystickdb_gui/routes/index.js b/node/dist/lib/joystickdb_gui/routes/index.js new file mode 100644 index 000000000..4fa14783b --- /dev/null +++ b/node/dist/lib/joystickdb_gui/routes/index.js @@ -0,0 +1 @@ +import d from"../middleware/auth.js";const e={"/joystickdb":(i={},s={})=>i.session?.joystickdb_user?s.redirect("/joystickdb/dashboard"):s.redirect("/joystickdb/login"),"/joystickdb/login":(i={},s={})=>{if(i.session?.joystickdb_user)return s.redirect("/joystickdb/dashboard");s.render("node/src/lib/joystickdb_gui/ui/pages/login.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{error:i.query.error}})},"/joystickdb/logout":{method:"POST",handler:(i={},s={})=>{i.session.joystickdb_user=null,i.session.joystickdb_client=null,s.redirect("/joystickdb/login")}},"/joystickdb/dashboard":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/dashboard.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{user:i.session.joystickdb_user}})}},"/joystickdb/databases":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/databases.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/databases/:database":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/collections.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database}})}},"/joystickdb/databases/:database/:collection":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/documents.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/query/:database/:collection":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/query.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/admin":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/index.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/users":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/users.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/replication":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/replication.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/stats":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/stats.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}}};var o=e;export{o as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/layouts/main.js b/node/dist/lib/joystickdb_gui/ui/layouts/main.js new file mode 100644 index 000000000..781489207 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/layouts/main.js @@ -0,0 +1,331 @@ +import e from"@joystick.js/ui";const b=e.component({css:` + :root { + --jdb-brand: #6366f1; + --jdb-success: #10b981; + --jdb-warning: #f59e0b; + --jdb-danger: #ef4444; + --jdb-info: #3b82f6; + --jdb-neutral-1: #ffffff; + --jdb-neutral-2: #f8fafc; + --jdb-neutral-3: #e2e8f0; + --jdb-neutral-4: #cbd5e1; + --jdb-neutral-5: #94a3b8; + --jdb-neutral-6: #64748b; + --jdb-neutral-7: #475569; + --jdb-neutral-8: #334155; + --jdb-neutral-9: #1e293b; + --jdb-border-radius: 6px; + --jdb-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --jdb-font-size-sm: 14px; + --jdb-font-size-base: 16px; + --jdb-font-size-lg: 18px; + --jdb-spacing-xs: 4px; + --jdb-spacing-sm: 8px; + --jdb-spacing-md: 16px; + --jdb-spacing-lg: 24px; + --jdb-spacing-xl: 32px; + } + .joystickdb-root { + font-family: var(--jdb-font-family); + color: var(--jdb-neutral-8); + margin: 0; + padding: 0; + } + .jdb-dashboard { + display: flex; + min-height: 100vh; + background: var(--jdb-neutral-2); + } + .jdb-sidebar { + width: 280px; + background: var(--jdb-neutral-1); + border-right: 1px solid var(--jdb-neutral-3); + display: flex; + flex-direction: column; + } + .jdb-sidebar-header { + padding: var(--jdb-spacing-lg); + border-bottom: 1px solid var(--jdb-neutral-3); + } + .jdb-sidebar-header h2 { + margin: 0 0 var(--jdb-spacing-xs) 0; + color: var(--jdb-brand); + font-size: 1.5rem; + font-weight: 700; + } + .jdb-sidebar-header p { + margin: 0; + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-sm); + } + .jdb-sidebar-nav { + flex: 1; + padding: var(--jdb-spacing-md); + overflow-y: auto; + } + .jdb-nav-section { + margin-bottom: var(--jdb-spacing-lg); + } + .jdb-nav-section h5 { + margin: 0 0 var(--jdb-spacing-sm) 0; + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-sm); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + } + .jdb-nav-list { + list-style: none; + margin: 0; + padding: 0; + } + .jdb-nav-list li { + margin-bottom: var(--jdb-spacing-xs); + } + .jdb-nav-link { + display: flex; + align-items: center; + gap: var(--jdb-spacing-sm); + padding: var(--jdb-spacing-sm) var(--jdb-spacing-md); + color: var(--jdb-neutral-7); + text-decoration: none; + border-radius: var(--jdb-border-radius); + transition: all 0.2s ease; + } + .jdb-nav-link:hover { + background: var(--jdb-neutral-2); + color: var(--jdb-neutral-8); + } + .jdb-nav-list li.active .jdb-nav-link { + background: var(--jdb-brand); + color: var(--jdb-neutral-1); + } + .jdb-sidebar-footer { + padding: var(--jdb-spacing-md); + border-top: 1px solid var(--jdb-neutral-3); + } + .jdb-user-info { + display: flex; + align-items: center; + gap: var(--jdb-spacing-sm); + } + .jdb-user-avatar { + width: 32px; + height: 32px; + background: var(--jdb-brand); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 14px; + } + .jdb-user-details { + flex: 1; + } + .jdb-user-details strong { + display: block; + color: var(--jdb-neutral-8); + font-size: var(--jdb-font-size-sm); + } + .jdb-user-details small { + color: var(--jdb-neutral-6); + font-size: 12px; + } + .jdb-btn-sm { + padding: var(--jdb-spacing-xs) var(--jdb-spacing-sm); + font-size: var(--jdb-font-size-sm); + background: var(--jdb-neutral-1); + border: 1px solid var(--jdb-neutral-4); + border-radius: var(--jdb-border-radius); + color: var(--jdb-neutral-8); + cursor: pointer; + text-decoration: none; + transition: all 0.2s; + } + .jdb-btn-sm:hover { + background: var(--jdb-neutral-2); + border-color: var(--jdb-neutral-5); + } + .jdb-main { + flex: 1; + display: flex; + flex-direction: column; + } + .jdb-header { + background: var(--jdb-neutral-1); + border-bottom: 1px solid var(--jdb-neutral-3); + padding: var(--jdb-spacing-md) var(--jdb-spacing-lg); + display: flex; + justify-content: space-between; + align-items: center; + } + .jdb-breadcrumbs { + display: flex; + align-items: center; + gap: var(--jdb-spacing-sm); + } + .jdb-breadcrumb { + color: var(--jdb-neutral-6); + text-decoration: none; + font-size: var(--jdb-font-size-sm); + } + .jdb-breadcrumb:hover { + color: var(--jdb-brand); + } + .jdb-breadcrumb-separator { + color: var(--jdb-neutral-5); + } + .jdb-content { + flex: 1; + padding: var(--jdb-spacing-lg); + overflow-y: auto; + } + .jdb-btn { + display: inline-flex; + align-items: center; + gap: var(--jdb-spacing-xs); + padding: var(--jdb-spacing-sm) var(--jdb-spacing-md); + border: 1px solid var(--jdb-neutral-4); + border-radius: var(--jdb-border-radius); + background: var(--jdb-neutral-1); + color: var(--jdb-neutral-8); + text-decoration: none; + cursor: pointer; + font-size: var(--jdb-font-size-base); + transition: all 0.2s; + } + .jdb-btn:hover { + border-color: var(--jdb-neutral-5); + background: var(--jdb-neutral-2); + } + .jdb-btn-primary { + background: var(--jdb-brand); + color: white; + border-color: var(--jdb-brand); + } + .jdb-btn-primary:hover { + background: #5855ec; + border-color: #5855ec; + } + .jdb-btn-success { + background: var(--jdb-success); + color: white; + border-color: var(--jdb-success); + } + .jdb-btn-success:hover { + background: #047857; + border-color: #047857; + } + .jdb-btn-danger { + background: var(--jdb-danger); + color: white; + border-color: var(--jdb-danger); + } + .jdb-btn-danger:hover { + background: #dc2626; + border-color: #dc2626; + } + .jdb-input { + padding: var(--jdb-spacing-sm); + border: 1px solid var(--jdb-neutral-4); + border-radius: var(--jdb-border-radius); + font-size: var(--jdb-font-size-base); + width: 100%; + } + .jdb-input:focus { + outline: none; + border-color: var(--jdb-brand); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); + } + .jdb-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: var(--jdb-border-radius); + overflow: hidden; + } + .jdb-table th, + .jdb-table td { + padding: var(--jdb-spacing-md); + text-align: left; + border-bottom: 1px solid var(--jdb-neutral-3); + } + .jdb-table th { + background: var(--jdb-neutral-2); + font-weight: 600; + } + .jdb-card { + background: white; + border-radius: var(--jdb-border-radius); + border: 1px solid var(--jdb-neutral-3); + overflow: hidden; + } + .jdb-card-header { + padding: var(--jdb-spacing-lg); + border-bottom: 1px solid var(--jdb-neutral-3); + display: flex; + justify-content: space-between; + align-items: center; + } + .jdb-card-content { + padding: var(--jdb-spacing-lg); + } + `,render:({component:r,props:d,url:a})=>` +
+ +
+
+
+ Dashboard + ${a.path!=="/joystickdb/dashboard"?'/':""} +
+
+ +
+
+
+ ${r(d.page,d)} +
+
+
+ `});var n=b;export{n as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/dashboard.js b/node/dist/lib/joystickdb_gui/ui/pages/dashboard.js new file mode 100644 index 000000000..b934a8a01 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/dashboard.js @@ -0,0 +1,81 @@ +import s from"@joystick.js/ui";const t=s.component({data:async(a={})=>({stats:await a.get("joystickdb_stats"),databases:await a.get("joystickdb_databases")}),css:` + .jdb-dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: var(--jdb-spacing-lg); + margin-bottom: var(--jdb-spacing-xl); + } + .jdb-stat-card { + background: white; + border-radius: var(--jdb-border-radius); + padding: var(--jdb-spacing-lg); + border: 1px solid var(--jdb-neutral-3); + } + .jdb-stat-title { + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-sm); + margin: 0 0 0.5rem 0; + text-transform: uppercase; + letter-spacing: 0.5px; + } + .jdb-stat-value { + font-size: 2rem; + font-weight: 700; + color: var(--jdb-neutral-8); + margin: 0; + } + .jdb-quick-actions { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: var(--jdb-spacing-md); + } + .jdb-dashboard-page h1 { + margin: 0 0 var(--jdb-spacing-lg) 0; + color: var(--jdb-neutral-8); + font-size: 1.75rem; + font-weight: 700; + } + `,render:({data:a,when:d})=>` +
+

Database Overview

+
+
+

Total Databases

+

${a?.databases?.databases?.length||0}

+
+
+

Active Connections

+

${a?.stats?.connections?.active||0}

+
+
+

Operations/sec

+

${a?.stats?.performance?.ops_per_second||0}

+
+
+

Memory Usage

+

${Math.round(a?.stats?.memory?.heapUsed/1024/1024)||0} MB

+
+
+ +
+ `});var i=t;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/login.js b/node/dist/lib/joystickdb_gui/ui/pages/login.js new file mode 100644 index 000000000..5989482d8 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/login.js @@ -0,0 +1,102 @@ +import a,{set as d}from"@joystick.js/ui";const t=a.component({state:{loading:!1,error:null},events:{"submit .login-form":async(r={},o={})=>{r.preventDefault(),o.set_state({loading:!0,error:null});try{await d("joystickdb_authenticate",{input:{username:r.target.username.value,password:r.target.password.value}}),window.location.href="/joystickdb/dashboard"}catch(e){o.set_state({loading:!1,error:e.message||"Authentication failed"})}}},css:` + .joystickdb-login { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: var(--jdb-neutral-2); + font-family: var(--jdb-font-family); + } + .jdb-login-card { + background: white; + padding: 2rem; + border-radius: var(--jdb-border-radius); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + max-width: 400px; + width: 100%; + border: 1px solid var(--jdb-neutral-3); + } + .jdb-login-title { + margin: 0 0 0.5rem 0; + color: var(--jdb-neutral-8); + font-size: 1.5rem; + font-weight: 600; + } + .jdb-login-subtitle { + margin: 0 0 2rem 0; + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-base); + } + .jdb-form-group { + margin-bottom: 1rem; + } + .jdb-label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; + color: var(--jdb-neutral-7); + } + .jdb-error { + background: #fef2f2; + color: #dc2626; + padding: 0.75rem; + border-radius: var(--jdb-border-radius); + margin-bottom: 1rem; + border: 1px solid #fecaca; + } + .jdb-btn-block { + width: 100%; + display: flex; + justify-content: center; + } + .jdb-btn:disabled { + opacity: 0.6; + cursor: not-allowed; + } + `,render:({state:r,props:o,when:e})=>` + + `});var n=t;export{n as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index d3e3a2cd7..02535116b 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -259,6 +259,40 @@ class App { await scan_directory(directory_path); } } + + // NOTE: Also scan for JoystickDB GUI components from the source directory + const joystickdb_source_path = `${process.cwd()}/node/src/lib/joystickdb_gui/ui`; + if (await path_exists(joystickdb_source_path)) { + const scan_joystickdb_directory = async (directory_path) => { + try { + const entries = await readdir(directory_path, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isDirectory()) { + // Recursively scan subdirectories (like admin/) + await scan_joystickdb_directory(`${directory_path}/${entry.name}`); + } else if (entry.isFile() && entry.name.endsWith('.js')) { + // Load direct .js component files + const file_path = `${directory_path}/${entry.name}`; + try { + const component = await dynamic_import(file_path); + // Create relative path that matches the route expectations + const relative_path = file_path.replace(`${process.cwd()}/`, ''); + + process._joystick_components[relative_path] = component; + } catch (error) { + console.warn(`Failed to load JoystickDB component: ${file_path}`, error.message); + } + } + } + } catch (error) { + console.warn(`Failed to scan JoystickDB directory: ${directory_path}`, error.message); + } + }; + + await scan_joystickdb_directory(`${joystickdb_source_path}/layouts`); + await scan_joystickdb_directory(`${joystickdb_source_path}/pages`); + } } on_after_start_server(express = {}) { @@ -545,6 +579,51 @@ class App { register_websockets(this.options.websockets, this); } + async register_joystickdb_gui() { + // NOTE: Automatically register JoystickDB GUI if JoystickDB is detected in databases + const has_joystickdb = process.databases && Object.keys(process.databases).some(key => + key === 'joystickdb' || key.startsWith('joystickdb_') + ); + + if (has_joystickdb) { + try { + const joystickdb_gui = await dynamic_import('../lib/joystickdb_gui/index.js'); + + // Register JoystickDB routes + const joystickdb_routes = Object.entries(joystickdb_gui.routes || {}); + for (let i = 0; i < joystickdb_routes?.length; i += 1) { + const [route_path, route_handler] = joystickdb_routes[i]; + const is_object_route = types.is_object(route_handler); + const is_function_route = types.is_function(route_handler); + + if (is_function_route) { + register_route_from_function(this.express.app, route_path, route_handler); + } + + if (is_object_route) { + register_route_from_object(this.express.app, route_path, route_handler); + } + } + + // Register JoystickDB API endpoints + const joystickdb_getters = joystickdb_gui.api.getters || {}; + const joystickdb_setters = joystickdb_gui.api.setters || {}; + + if (Object.keys(joystickdb_getters).length > 0) { + register_getters(this.express.app, Object.entries(joystickdb_getters), this?.options?.api?.context, this?.options?.api?.options); + } + + if (Object.keys(joystickdb_setters).length > 0) { + register_setters(this.express.app, Object.entries(joystickdb_setters), this?.options?.api?.context, this?.options?.api?.options); + } + + console.log('JoystickDB GUI registered automatically at /joystickdb'); + } catch (error) { + console.warn('Failed to register JoystickDB GUI:', error.message); + } + } + } + async start() { // NOTE: Always run this first so we can cache translations in memory. await this.load_translations(); @@ -567,6 +646,7 @@ class App { this.register_accounts(); this.register_api(); this.register_routes(); + await this.register_joystickdb_gui(); this.register_dynamic_pages(); this.register_uploaders(); this.register_fixtures(); diff --git a/node/src/lib/joystickdb_gui/api/getters.js b/node/src/lib/joystickdb_gui/api/getters.js new file mode 100644 index 000000000..59bbbca1b --- /dev/null +++ b/node/src/lib/joystickdb_gui/api/getters.js @@ -0,0 +1,183 @@ +import JoystickDBGUIClient from '../client/joystickdb_client.js'; + +const getters = { + joystickdb_stats: { + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + return await client.get_stats(); + }, + }, + joystickdb_databases: { + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + return await client.list_databases(); + }, + }, + joystickdb_collections: { + input: { + database: { + type: 'string', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + return await db.list_collections(); + }, + }, + joystickdb_documents: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + limit: { + type: 'integer', + required: false, + max: 1000, + }, + skip: { + type: 'integer', + required: false, + }, + filter: { + type: 'object', + required: false, + }, + sort: { + type: 'object', + required: false, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + const options = { + limit: input.limit || 20, + }; + if (input.skip) options.skip = input.skip; + if (input.sort) options.sort = input.sort; + const filter = input.filter || {}; + const documents = await collection.find(filter, options); + const total = await collection.count_documents(filter); + return { + documents, + total, + }; + }, + }, + joystickdb_indexes: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + return await collection.get_indexes(); + }, + }, + joystickdb_replication_status: { + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + try { + return await client.admin_operation({ + operation: 'get_sync_status', + }); + } catch (error) { + return { error: error.message }; + } + }, + }, + joystickdb_document_by_id: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + document_id: { + type: 'string', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const document = await db.get_document(input.collection, input.document_id); + return document; + }, + }, + joystickdb_backup_list: { + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + get: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + return await client.list_backups(); + }, + }, +}; + +export default getters; diff --git a/node/src/lib/joystickdb_gui/api/index.js b/node/src/lib/joystickdb_gui/api/index.js new file mode 100644 index 000000000..2ce341467 --- /dev/null +++ b/node/src/lib/joystickdb_gui/api/index.js @@ -0,0 +1,13 @@ +import joystickdb_getters from './getters.js'; +import joystickdb_setters from './setters.js'; + +const joystickdb_api = { + getters: { + ...joystickdb_getters, + }, + setters: { + ...joystickdb_setters, + }, +}; + +export default joystickdb_api; diff --git a/node/src/lib/joystickdb_gui/api/setters.js b/node/src/lib/joystickdb_gui/api/setters.js new file mode 100644 index 000000000..01e12547d --- /dev/null +++ b/node/src/lib/joystickdb_gui/api/setters.js @@ -0,0 +1,226 @@ +import JoystickDBGUIClient from '../client/joystickdb_client.js'; + +const setters = { + joystickdb_authenticate: { + input: { + username: { + type: 'string', + required: true, + }, + password: { + type: 'string', + required: true, + }, + }, + set: async (input = {}, context = {}) => { + try { + const gui_client = new JoystickDBGUIClient({ + username: input.username, + password: input.password, + }); + const client = await gui_client.connect(); + context.req.session.joystickdb_user = { + username: input.username, + }; + context.req.session.joystickdb_client = client; + return { success: true, username: input.username }; + } catch (error) { + throw new Error(`Authentication failed: ${error.message}`); + } + }, + }, + joystickdb_create_document: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + document: { + type: 'object', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + return await collection.insert_one(input.document); + }, + }, + joystickdb_update_document: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + filter: { + type: 'object', + required: true, + }, + update: { + type: 'object', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + return await collection.update_one(input.filter, input.update); + }, + }, + joystickdb_delete_document: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + filter: { + type: 'object', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + return await collection.delete_one(input.filter); + }, + }, + joystickdb_create_index: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + field: { + type: 'string', + required: true, + }, + options: { + type: 'object', + required: false, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + return await collection.create_index(input.field, input.options || {}); + }, + }, + joystickdb_drop_index: { + input: { + database: { + type: 'string', + required: true, + }, + collection: { + type: 'string', + required: true, + }, + field: { + type: 'string', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + const db = client.db(input.database); + const collection = db.collection(input.collection); + return await collection.drop_index(input.field); + }, + }, + joystickdb_force_sync: { + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + return await client.admin_operation({ + operation: 'force_sync', + }); + }, + }, + joystickdb_create_backup: { + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + return await client.backup_now(); + }, + }, + joystickdb_restore_backup: { + input: { + backup_name: { + type: 'string', + required: true, + }, + }, + authorized: (input = {}, context = {}) => { + return !!context?.req?.session?.joystickdb_user; + }, + set: async (input = {}, context = {}) => { + const client = context?.req?.session?.joystickdb_client; + if (!client) { + throw new Error('No JoystickDB connection available'); + } + return await client.restore_backup(input.backup_name); + }, + }, +}; + +export default setters; diff --git a/node/src/lib/joystickdb_gui/client/joystickdb_client.js b/node/src/lib/joystickdb_gui/client/joystickdb_client.js new file mode 100644 index 000000000..e6459d352 --- /dev/null +++ b/node/src/lib/joystickdb_gui/client/joystickdb_client.js @@ -0,0 +1,45 @@ +import joystickdb from '../../../lib/joystickdb/index.js'; + +class JoystickDBGUIClient { + constructor(session_credentials) { + this.credentials = session_credentials; + this.client = null; + } + + async connect() { + if (this.client) { + return this.client; + } + try { + this.client = joystickdb.client({ + port: 1983, + authentication: this.credentials, + timeout: 5000, + }); + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error('Connection timeout')); + }, 5000); + this.client.on('authenticated', () => { + clearTimeout(timeout); + resolve(this.client); + }); + this.client.on('error', (error) => { + clearTimeout(timeout); + reject(error); + }); + }); + } catch (error) { + throw new Error(`Failed to connect to JoystickDB: ${error.message}`); + } + } + + async disconnect() { + if (this.client) { + this.client.disconnect(); + this.client = null; + } + } +} + +export default JoystickDBGUIClient; diff --git a/node/src/lib/joystickdb_gui/index.js b/node/src/lib/joystickdb_gui/index.js new file mode 100644 index 000000000..3c35ae39e --- /dev/null +++ b/node/src/lib/joystickdb_gui/index.js @@ -0,0 +1,9 @@ +import joystickdb_routes from './routes/index.js'; +import joystickdb_api from './api/index.js'; + +const joystickdb_gui = { + routes: joystickdb_routes, + api: joystickdb_api, +}; + +export default joystickdb_gui; diff --git a/node/src/lib/joystickdb_gui/middleware/auth.js b/node/src/lib/joystickdb_gui/middleware/auth.js new file mode 100644 index 000000000..08f9715f4 --- /dev/null +++ b/node/src/lib/joystickdb_gui/middleware/auth.js @@ -0,0 +1,10 @@ +const joystickdb_auth_middleware = (req = {}, res = {}, next = {}) => { + if (!req.session?.joystickdb_user || !req.session?.joystickdb_client) { + return res.redirect('/joystickdb/login?error=authentication_required'); + } + req.joystickdb_client = req.session.joystickdb_client; + req.joystickdb_user = req.session.joystickdb_user; + next(); +}; + +export default joystickdb_auth_middleware; diff --git a/node/src/lib/joystickdb_gui/routes/index.js b/node/src/lib/joystickdb_gui/routes/index.js new file mode 100644 index 000000000..fc3a8bbd2 --- /dev/null +++ b/node/src/lib/joystickdb_gui/routes/index.js @@ -0,0 +1,117 @@ +import joystickdb_auth_middleware from '../middleware/auth.js'; + +const joystickdb_routes = { + '/joystickdb': (req = {}, res = {}) => { + if (req.session?.joystickdb_user) { + return res.redirect('/joystickdb/dashboard'); + } + return res.redirect('/joystickdb/login'); + }, + '/joystickdb/login': (req = {}, res = {}) => { + if (req.session?.joystickdb_user) { + return res.redirect('/joystickdb/dashboard'); + } + res.render('node/src/lib/joystickdb_gui/ui/pages/login.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + props: { + error: req.query.error, + }, + }); + }, + '/joystickdb/logout': { + method: 'POST', + handler: (req = {}, res = {}) => { + req.session.joystickdb_user = null; + req.session.joystickdb_client = null; + res.redirect('/joystickdb/login'); + }, + }, + '/joystickdb/dashboard': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/dashboard.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + props: { + user: req.session.joystickdb_user, + }, + }); + }, + }, + '/joystickdb/databases': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/databases.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + }); + }, + }, + '/joystickdb/databases/:database': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/collections.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + props: { + database: req.params.database, + }, + }); + }, + }, + '/joystickdb/databases/:database/:collection': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/documents.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + props: { + database: req.params.database, + collection: req.params.collection, + }, + }); + }, + }, + '/joystickdb/query/:database/:collection': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/query.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + props: { + database: req.params.database, + collection: req.params.collection, + }, + }); + }, + }, + '/joystickdb/admin': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/admin/index.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + }); + }, + }, + '/joystickdb/admin/users': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/admin/users.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + }); + }, + }, + '/joystickdb/admin/replication': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/admin/replication.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + }); + }, + }, + '/joystickdb/admin/stats': { + middleware: [joystickdb_auth_middleware], + handler: (req = {}, res = {}) => { + res.render('node/src/lib/joystickdb_gui/ui/pages/admin/stats.js', { + layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + }); + }, + }, +}; + +export default joystickdb_routes; diff --git a/node/src/lib/joystickdb_gui/ui/layouts/main.js b/node/src/lib/joystickdb_gui/ui/layouts/main.js new file mode 100644 index 000000000..aaaee0edc --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/layouts/main.js @@ -0,0 +1,340 @@ +import joystick from '@joystick.js/ui'; + +const JoystickDBLayout = joystick.component({ + css: ` + :root { + --jdb-brand: #6366f1; + --jdb-success: #10b981; + --jdb-warning: #f59e0b; + --jdb-danger: #ef4444; + --jdb-info: #3b82f6; + --jdb-neutral-1: #ffffff; + --jdb-neutral-2: #f8fafc; + --jdb-neutral-3: #e2e8f0; + --jdb-neutral-4: #cbd5e1; + --jdb-neutral-5: #94a3b8; + --jdb-neutral-6: #64748b; + --jdb-neutral-7: #475569; + --jdb-neutral-8: #334155; + --jdb-neutral-9: #1e293b; + --jdb-border-radius: 6px; + --jdb-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --jdb-font-size-sm: 14px; + --jdb-font-size-base: 16px; + --jdb-font-size-lg: 18px; + --jdb-spacing-xs: 4px; + --jdb-spacing-sm: 8px; + --jdb-spacing-md: 16px; + --jdb-spacing-lg: 24px; + --jdb-spacing-xl: 32px; + } + .joystickdb-root { + font-family: var(--jdb-font-family); + color: var(--jdb-neutral-8); + margin: 0; + padding: 0; + } + .jdb-dashboard { + display: flex; + min-height: 100vh; + background: var(--jdb-neutral-2); + } + .jdb-sidebar { + width: 280px; + background: var(--jdb-neutral-1); + border-right: 1px solid var(--jdb-neutral-3); + display: flex; + flex-direction: column; + } + .jdb-sidebar-header { + padding: var(--jdb-spacing-lg); + border-bottom: 1px solid var(--jdb-neutral-3); + } + .jdb-sidebar-header h2 { + margin: 0 0 var(--jdb-spacing-xs) 0; + color: var(--jdb-brand); + font-size: 1.5rem; + font-weight: 700; + } + .jdb-sidebar-header p { + margin: 0; + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-sm); + } + .jdb-sidebar-nav { + flex: 1; + padding: var(--jdb-spacing-md); + overflow-y: auto; + } + .jdb-nav-section { + margin-bottom: var(--jdb-spacing-lg); + } + .jdb-nav-section h5 { + margin: 0 0 var(--jdb-spacing-sm) 0; + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-sm); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + } + .jdb-nav-list { + list-style: none; + margin: 0; + padding: 0; + } + .jdb-nav-list li { + margin-bottom: var(--jdb-spacing-xs); + } + .jdb-nav-link { + display: flex; + align-items: center; + gap: var(--jdb-spacing-sm); + padding: var(--jdb-spacing-sm) var(--jdb-spacing-md); + color: var(--jdb-neutral-7); + text-decoration: none; + border-radius: var(--jdb-border-radius); + transition: all 0.2s ease; + } + .jdb-nav-link:hover { + background: var(--jdb-neutral-2); + color: var(--jdb-neutral-8); + } + .jdb-nav-list li.active .jdb-nav-link { + background: var(--jdb-brand); + color: var(--jdb-neutral-1); + } + .jdb-sidebar-footer { + padding: var(--jdb-spacing-md); + border-top: 1px solid var(--jdb-neutral-3); + } + .jdb-user-info { + display: flex; + align-items: center; + gap: var(--jdb-spacing-sm); + } + .jdb-user-avatar { + width: 32px; + height: 32px; + background: var(--jdb-brand); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 14px; + } + .jdb-user-details { + flex: 1; + } + .jdb-user-details strong { + display: block; + color: var(--jdb-neutral-8); + font-size: var(--jdb-font-size-sm); + } + .jdb-user-details small { + color: var(--jdb-neutral-6); + font-size: 12px; + } + .jdb-btn-sm { + padding: var(--jdb-spacing-xs) var(--jdb-spacing-sm); + font-size: var(--jdb-font-size-sm); + background: var(--jdb-neutral-1); + border: 1px solid var(--jdb-neutral-4); + border-radius: var(--jdb-border-radius); + color: var(--jdb-neutral-8); + cursor: pointer; + text-decoration: none; + transition: all 0.2s; + } + .jdb-btn-sm:hover { + background: var(--jdb-neutral-2); + border-color: var(--jdb-neutral-5); + } + .jdb-main { + flex: 1; + display: flex; + flex-direction: column; + } + .jdb-header { + background: var(--jdb-neutral-1); + border-bottom: 1px solid var(--jdb-neutral-3); + padding: var(--jdb-spacing-md) var(--jdb-spacing-lg); + display: flex; + justify-content: space-between; + align-items: center; + } + .jdb-breadcrumbs { + display: flex; + align-items: center; + gap: var(--jdb-spacing-sm); + } + .jdb-breadcrumb { + color: var(--jdb-neutral-6); + text-decoration: none; + font-size: var(--jdb-font-size-sm); + } + .jdb-breadcrumb:hover { + color: var(--jdb-brand); + } + .jdb-breadcrumb-separator { + color: var(--jdb-neutral-5); + } + .jdb-content { + flex: 1; + padding: var(--jdb-spacing-lg); + overflow-y: auto; + } + .jdb-btn { + display: inline-flex; + align-items: center; + gap: var(--jdb-spacing-xs); + padding: var(--jdb-spacing-sm) var(--jdb-spacing-md); + border: 1px solid var(--jdb-neutral-4); + border-radius: var(--jdb-border-radius); + background: var(--jdb-neutral-1); + color: var(--jdb-neutral-8); + text-decoration: none; + cursor: pointer; + font-size: var(--jdb-font-size-base); + transition: all 0.2s; + } + .jdb-btn:hover { + border-color: var(--jdb-neutral-5); + background: var(--jdb-neutral-2); + } + .jdb-btn-primary { + background: var(--jdb-brand); + color: white; + border-color: var(--jdb-brand); + } + .jdb-btn-primary:hover { + background: #5855ec; + border-color: #5855ec; + } + .jdb-btn-success { + background: var(--jdb-success); + color: white; + border-color: var(--jdb-success); + } + .jdb-btn-success:hover { + background: #047857; + border-color: #047857; + } + .jdb-btn-danger { + background: var(--jdb-danger); + color: white; + border-color: var(--jdb-danger); + } + .jdb-btn-danger:hover { + background: #dc2626; + border-color: #dc2626; + } + .jdb-input { + padding: var(--jdb-spacing-sm); + border: 1px solid var(--jdb-neutral-4); + border-radius: var(--jdb-border-radius); + font-size: var(--jdb-font-size-base); + width: 100%; + } + .jdb-input:focus { + outline: none; + border-color: var(--jdb-brand); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); + } + .jdb-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: var(--jdb-border-radius); + overflow: hidden; + } + .jdb-table th, + .jdb-table td { + padding: var(--jdb-spacing-md); + text-align: left; + border-bottom: 1px solid var(--jdb-neutral-3); + } + .jdb-table th { + background: var(--jdb-neutral-2); + font-weight: 600; + } + .jdb-card { + background: white; + border-radius: var(--jdb-border-radius); + border: 1px solid var(--jdb-neutral-3); + overflow: hidden; + } + .jdb-card-header { + padding: var(--jdb-spacing-lg); + border-bottom: 1px solid var(--jdb-neutral-3); + display: flex; + justify-content: space-between; + align-items: center; + } + .jdb-card-content { + padding: var(--jdb-spacing-lg); + } + `, + render: ({ component, props, url }) => { + return ` +
+ +
+
+
+ Dashboard + ${url.path !== '/joystickdb/dashboard' ? '/' : ''} +
+
+ +
+
+
+ ${component(props.page, props)} +
+
+
+ `; + }, +}); + +export default JoystickDBLayout; diff --git a/node/src/lib/joystickdb_gui/ui/pages/dashboard.js b/node/src/lib/joystickdb_gui/ui/pages/dashboard.js new file mode 100644 index 000000000..1919c6009 --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/dashboard.js @@ -0,0 +1,96 @@ +import joystick from '@joystick.js/ui'; + +const JoystickDBDashboard = joystick.component({ + data: async (api = {}) => { + return { + stats: await api.get('joystickdb_stats'), + databases: await api.get('joystickdb_databases'), + }; + }, + css: ` + .jdb-dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: var(--jdb-spacing-lg); + margin-bottom: var(--jdb-spacing-xl); + } + .jdb-stat-card { + background: white; + border-radius: var(--jdb-border-radius); + padding: var(--jdb-spacing-lg); + border: 1px solid var(--jdb-neutral-3); + } + .jdb-stat-title { + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-sm); + margin: 0 0 0.5rem 0; + text-transform: uppercase; + letter-spacing: 0.5px; + } + .jdb-stat-value { + font-size: 2rem; + font-weight: 700; + color: var(--jdb-neutral-8); + margin: 0; + } + .jdb-quick-actions { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: var(--jdb-spacing-md); + } + .jdb-dashboard-page h1 { + margin: 0 0 var(--jdb-spacing-lg) 0; + color: var(--jdb-neutral-8); + font-size: 1.75rem; + font-weight: 700; + } + `, + render: ({ data, when }) => { + return ` +
+

Database Overview

+
+
+

Total Databases

+

${data?.databases?.databases?.length || 0}

+
+
+

Active Connections

+

${data?.stats?.connections?.active || 0}

+
+
+

Operations/sec

+

${data?.stats?.performance?.ops_per_second || 0}

+
+
+

Memory Usage

+

${Math.round(data?.stats?.memory?.heapUsed / 1024 / 1024) || 0} MB

+
+
+ +
+ `; + }, +}); + +export default JoystickDBDashboard; diff --git a/node/src/lib/joystickdb_gui/ui/pages/login.js b/node/src/lib/joystickdb_gui/ui/pages/login.js new file mode 100644 index 000000000..e4224e526 --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/login.js @@ -0,0 +1,135 @@ +import joystick, { set } from '@joystick.js/ui'; + +const JoystickDBLogin = joystick.component({ + state: { + loading: false, + error: null, + }, + events: { + 'submit .login-form': async (event = {}, instance = {}) => { + event.preventDefault(); + instance.set_state({ loading: true, error: null }); + try { + await set('joystickdb_authenticate', { + input: { + username: event.target.username.value, + password: event.target.password.value, + }, + }); + window.location.href = '/joystickdb/dashboard'; + } catch (error) { + instance.set_state({ + loading: false, + error: error.message || 'Authentication failed', + }); + } + }, + }, + css: ` + .joystickdb-login { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: var(--jdb-neutral-2); + font-family: var(--jdb-font-family); + } + .jdb-login-card { + background: white; + padding: 2rem; + border-radius: var(--jdb-border-radius); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + max-width: 400px; + width: 100%; + border: 1px solid var(--jdb-neutral-3); + } + .jdb-login-title { + margin: 0 0 0.5rem 0; + color: var(--jdb-neutral-8); + font-size: 1.5rem; + font-weight: 600; + } + .jdb-login-subtitle { + margin: 0 0 2rem 0; + color: var(--jdb-neutral-6); + font-size: var(--jdb-font-size-base); + } + .jdb-form-group { + margin-bottom: 1rem; + } + .jdb-label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; + color: var(--jdb-neutral-7); + } + .jdb-error { + background: #fef2f2; + color: #dc2626; + padding: 0.75rem; + border-radius: var(--jdb-border-radius); + margin-bottom: 1rem; + border: 1px solid #fecaca; + } + .jdb-btn-block { + width: 100%; + display: flex; + justify-content: center; + } + .jdb-btn:disabled { + opacity: 0.6; + cursor: not-allowed; + } + `, + render: ({ state, props, when }) => { + return ` + + `; + }, +}); + +export default JoystickDBLogin; From 9f9d668dfb7103052699b3b8dd6253ec813bfc48 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Thu, 2 Oct 2025 19:16:58 -0500 Subject: [PATCH 02/79] release 0.0.0-canary.2277 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 2ed270dc3..9988fc36f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index db7b74760..e6f3fa127 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2276", + "canary_version": "0.0.0-canary.2277", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 37ad958d3720f7712837346a8ee67e1ef9d66bb3 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Thu, 2 Oct 2025 19:17:01 -0500 Subject: [PATCH 03/79] release 0.0.0-canary.2277 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 905e92fab..eb100f5cc 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 873153b5b..d30f9a536 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2276", + "canary_version": "0.0.0-canary.2277", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 59a134491744551b397f5732c8e6edb763c18845 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Thu, 2 Oct 2025 19:17:04 -0500 Subject: [PATCH 04/79] release 0.0.0-canary.2277 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index f397369f1..fa15601c0 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index bc7bf04d0..0cc3670c0 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2276", + "canary_version": "0.0.0-canary.2277", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From a88b10ac8d5b111f4e9d76b217cb433c61b3e3fa Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Thu, 2 Oct 2025 19:17:07 -0500 Subject: [PATCH 05/79] release 0.0.0-canary.2277 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index f74721dc1..52add0242 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 065962549..670ed3206 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2276", + "canary_version": "0.0.0-canary.2277", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 846339cb0990e5c63199ed4d5824ef71fb445630 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Thu, 2 Oct 2025 19:17:10 -0500 Subject: [PATCH 06/79] release 0.0.0-canary.2277 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 22c839cc0..4caee3601 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2276", + "version": "0.0.0-canary.2277", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index 01421924e..7174e2921 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2276", + "canary_version": "0.0.0-canary.2277", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From cc21a85fa0e3158fe7a6db9b6d6b1598f90b71a4 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:29:02 -0500 Subject: [PATCH 07/79] wip debugging joystickdb gui --- .joystick/PROCESS_ID | 1 + node/dist/app/index.js | 2 +- node/src/app/index.js | 67 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 .joystick/PROCESS_ID diff --git a/.joystick/PROCESS_ID b/.joystick/PROCESS_ID new file mode 100644 index 000000000..5a136e687 --- /dev/null +++ b/.joystick/PROCESS_ID @@ -0,0 +1 @@ +DW0BVIX0KmGLwr08FQEjsyxPVdauu8t4 \ No newline at end of file diff --git a/node/dist/app/index.js b/node/dist/app/index.js index bbac898ce..62f4921b7 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import F from"http";import D from"fs";import E from"./api/accounts/authenticated.js";import I from"./api/accounts/login.js";import N from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import W from"./api/accounts/reset_password.js";import S from"./api/accounts/signup.js";import J from"./api/accounts/user.js";import T from"./api/accounts/verify_email.js";import A from"./api/test/accounts/delete.js";import B from"./api/test/accounts/signup.js";import U from"./api/test/bootstrap.js";import P from"./api/test/process.js";import C from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import G from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import j from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import k from"./api/register_getters.js";import x from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import v from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import l from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:m,readdir:g}=D.promises,lt=M();class dt{constructor(s={}){F.globalAgent.maxSockets=1/0,Q(s?.events),et(this,s),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:s,external_process_ids:[],track_external_process:(t="")=>{process.send({external_process_id:t}),process.joystick.external_process_ids.push(t)}}}async connect_databases(){const s=lt?.config?.databases;for(let t=0;ti?.provider===e?.provider)?.length>1;await it(i,o,a)}if(s?.length>0){const t=w("queues"),i=w("users");process.databases._queues=t?.connection,process.databases._users=i?.connection;const o=[t,i],a=o?.filter(r=>r?.provider==="mongodb")?.map(r=>r?.database_type);await L(a);const e=o?.filter(r=>r?.provider==="postgresql")?.map(r=>r?.database_type);await H(e),await G(e)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const s=f();if(!await p(s))return;process._joystick_translations={normal:{files:[],path:`${s}i18n`,cache:{}},email:{files:[],path:`${s}i18n/email`,cache:{}}};const t=async i=>{const o=process._joystick_translations[i];if(await p(o.path))try{const a=await g(o.path);o.files=a.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of o.files){const r=`${o.path}/${e}`;try{const n=await h(r);o.cache[e]=n}catch(n){console.warn(`Failed to load translation file: ${r}`,n.message)}}}catch(a){console.warn(`Failed to scan translation directory: ${o.path}`,a.message)}};await t("normal"),await t("email")}async load_email_templates(){const t=`${f()}email`;if(await p(t)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await g(t),o=i.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of o){const r=`${t}/${e}`,n=e.replace(".js","");try{const c=await h(r);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${r}`,c.message)}}const a=i.filter(e=>e.startsWith("base")&&(e.endsWith(".html")||e.endsWith(".css")));for(const e of a){const r=`${t}/${e}`;try{const n=await m(r,"utf-8");process._joystick_email_base_files[e]=n}catch(n){console.warn(`Failed to load email base file: ${r}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${t}`,i.message)}}}async load_ui(){const s=f();if(!await p(s))return;process._joystick_html=await m("index.html","utf-8"),process._joystick_components={};const t=async a=>{try{const e=await g(a,{withFileTypes:!0});for(const r of e)if(r.isDirectory()){const c=`${`${a}/${r.name}`}/index.js`;if(await p(c))try{const _=await h(c),u=c.replace(`${s}`,"");process._joystick_components[u]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(e){console.warn(`Failed to scan directory: ${a}`,e.message)}},i=[`${s}ui/components`,`${s}ui/layouts`,`${s}ui/pages`];for(const a of i)await p(a)&&await t(a);const o=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(o)){const a=async e=>{try{const r=await g(e,{withFileTypes:!0});for(const n of r)if(n.isDirectory())await a(`${e}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${e}/${n.name}`;try{const _=await h(c),u=c.replace(`${process.cwd()}/`,"");process._joystick_components[u]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(r){console.warn(`Failed to scan JoystickDB directory: ${e}`,r.message)}};await a(`${o}/layouts`),await a(`${o}/pages`)}}on_after_start_server(s={}){process.on("message",t=>{if(typeof t=="string"){const i=JSON.parse(t);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(t))}}),console.log(`App running at: http://localhost:${s.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",J),this.express.app.post("/api/_accounts/login",I),this.express.app.post("/api/_accounts/logout",N),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",W),this.express.app.post("/api/_accounts/signup",S),this.express.app.get("/api/_accounts/verify-email",T)}register_api(){const s=this?.options?.api?.getters,t=this?.options?.api?.setters,i=this?.options?.api?.options,o=this?.options?.api?.context;s&&l.is_object(s)&&Object.keys(s||{}).length>0&&k(this.express.app,Object.entries(s||{}),o,i),t&&l.is_object(t)&&Object.keys(t||{}).length>0&&v(this.express.app,Object.entries(t||{}),o,i)}register_caches(){process.caches={},l.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(s={},t={})=>{const i=f(),o=_t(s?.body?.page),a=`${i}/${o}`;if(!s?.body?.page||!await p(a))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${a}.`),t);const e=await h(a);if(e){const r=X(s?.body?.route_pattern||"",s?.body?.path),n=z({params:r?.params||{},query:s?.body?.query_params||{},url:s?.body?.path,headers:s?.headers,context:s?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:e,api_schema:this?.options?.api,component_options:{props:s?.body?.props},req:n});let _={};try{const q=`${f()}i18n`,b=process._joystick_translations?.normal?.files||[];b.length>0&&(_=await K({req:n,language_files_path:q,language_files:b,render_component_path:o}))}catch(u){console.warn("Failed to load translations for dynamic page:",u.message)}return t.status(200).send({data:c,req:n,url:{params:r?.params||{},query:s?.body?.query_params||{},path:s?.body?.path,route:s?.body?.route_pattern||s?.body?.path},i18n:_})}return t.status(200).send({})})}register_fixtures(){l.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){l.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const t=await p("private/mod/mod_version.txt")&&(await m("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,o,a,e={},r={};if(t==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await m("private/mod/mod-light-plus.min.css","utf-8")||"",o=await p("private/mod/mod-dark-plus.min.css")&&await m("private/mod/mod-dark-plus.min.css","utf-8")||"",a={esm:await p("lib/mod-plus.esm.min.js")&&await m("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await m("lib/mod-plus.iife.min.js","utf-8")||""},e=await j();const n=await y("free"),c=await y("plus");r={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await m("private/mod/mod-light.min.css","utf-8")||"",o=await p("private/mod/mod-dark.min.css")&&await m("private/mod/mod-dark.min.css","utf-8")||"",a={esm:await p("lib/mod.esm.min.js")&&await m("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await m("lib/mod.iife.min.js","utf-8")||""},e=await j(),r={...await y("free")||{}};this.mod={version:t,css:{light:i,dark:o},js:a,globals:e,components:r}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(s={},t={})=>s?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?t.status(403).send("403 - You are not allowed to access this endpoint."):t.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(l.is_object(this.options.queues)){const s=Object.entries(this.options.queues||{});for(let t=0;tU(s,t,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",A),this.express.app.post("/api/_test/accounts/signup",B),this.express.app.post("/api/_test/queues",(s={},t={})=>C(s,t,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){if(process.databases&&Object.keys(process.databases).some(t=>t==="joystickdb"||t.startsWith("joystickdb_")))try{const t=await h("../lib/joystickdb_gui/index.js"),i=Object.entries(t.routes||{});for(let e=0;e0&&k(this.express.app,Object.entries(o),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&v(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered automatically at /joystickdb")}catch(t){console.warn("Failed to register JoystickDB GUI:",t.message)}}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),await this.register_joystickdb_gui(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const O=async(d={})=>{const s=new dt(d);return await s.start(d),s},ut=(d={})=>new Promise(async s=>{if(d?.cluster)pt(async()=>{const t=await O(d);return s(t.express)});else{const t=await O(d);return s(t.express)}});var _s=ut;export{_s as default}; +import q from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import R from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import B from"./api/accounts/recover_password.js";import U from"./api/accounts/reset_password.js";import N from"./api/accounts/signup.js";import W from"./api/accounts/user.js";import A from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import T from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import C from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import H from"./databases/postgresql/create_indexes.js";import V from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import Y from"./generate_machine_id.js";import z from"./generate_process_id.js";import K from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import j from"./databases/get_target_database_connection.js";import Q from"../lib/get_translations.js";import M from"./handle_process_errors.js";import X from"./settings/load.js";import Z from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import tt from"./push/index.js";import st from"./push/logger.js";import et from"./queues/index.js";import b from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import ot from"./register_app_options.js";import it from"./cron_jobs/register.js";import at from"./databases/register_database.js";import x from"./api/register_getters.js";import $ from"./routes/register_route_from_function.js";import v from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import rt from"./uploaders/register.js";import nt from"./websockets/register.js";import ct from"./ssr/index.js";import pt from"./start_express.js";import _t from"./start_node_as_cluster.js";import lt from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:l,readdir:y}=I.promises,D=X();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,M(t?.events),ot(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=D?.config?.databases||this.options?.config?.databases;console.log("Connecting databases:",{databases_from_settings:t,databases_count:t?.length||0,from_app_settings:!!D?.config?.databases,from_options:!!this.options?.config?.databases});for(let s=0;so?.provider===e?.provider)?.length>1;console.log(`Registering database ${s+1}:`,{name:o?.name,provider:o?.provider,port:i,has_multiple:a});try{await at(o,i,a),console.log(`Database ${o?.provider} registered successfully`)}catch(e){console.error(`Failed to register database ${o?.provider}:`,e)}}if(console.log("Final process.databases state:",{databases_exist:!!process.databases,database_keys:process.databases?Object.keys(process.databases):[]}),t?.length>0){const s=j("queues"),o=j("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const i=[s,o],a=i?.filter(r=>r?.provider==="mongodb")?.map(r=>r?.database_type);await L(a);const e=i?.filter(r=>r?.provider==="postgresql")?.map(r=>r?.database_type);await V(e),await H(e)}}async generate_machine_id(){this.joystick_machine_id=await Y()}async generate_process_id(){this.joystick_process_id=await z()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const i=process._joystick_translations[o];if(await p(i.path))try{const a=await y(i.path);i.files=a.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of i.files){const r=`${i.path}/${e}`;try{const n=await h(r);i.cache[e]=n}catch(n){console.warn(`Failed to load translation file: ${r}`,n.message)}}}catch(a){console.warn(`Failed to scan translation directory: ${i.path}`,a.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await y(s),i=o.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of i){const r=`${s}/${e}`,n=e.replace(".js","");try{const c=await h(r);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${r}`,c.message)}}const a=o.filter(e=>e.startsWith("base")&&(e.endsWith(".html")||e.endsWith(".css")));for(const e of a){const r=`${s}/${e}`;try{const n=await l(r,"utf-8");process._joystick_email_base_files[e]=n}catch(n){console.warn(`Failed to load email base file: ${r}`,n.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await l("index.html","utf-8"),process._joystick_components={};const s=async a=>{try{const e=await y(a,{withFileTypes:!0});for(const r of e)if(r.isDirectory()){const c=`${`${a}/${r.name}`}/index.js`;if(await p(c))try{const _=await h(c),d=c.replace(`${t}`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(e){console.warn(`Failed to scan directory: ${a}`,e.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const a of o)await p(a)&&await s(a);const i=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(i)){const a=async e=>{try{const r=await y(e,{withFileTypes:!0});for(const n of r)if(n.isDirectory())await a(`${e}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${e}/${n.name}`;try{const _=await h(c),d=c.replace(`${process.cwd()}/`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(r){console.warn(`Failed to scan JoystickDB directory: ${e}`,r.message)}};await a(`${i}/layouts`),await a(`${i}/pages`)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",W),this.express.app.post("/api/_accounts/login",R),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",B),this.express.app.post("/api/_accounts/reset-password",U),this.express.app.post("/api/_accounts/signup",N),this.express.app.get("/api/_accounts/verify-email",A)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,i=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),i,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),i,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){it(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),i=lt(t?.body?.page),a=`${o}/${i}`;if(!t?.body?.page||!await p(a))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${a}.`),s);const e=await h(a);if(e){const r=Z(t?.body?.route_pattern||"",t?.body?.path),n=K({params:r?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await ct({is_dynamic_page_render:!0,component_to_render:e,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let _={};try{const g=`${f()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(_=await Q({req:n,language_files_path:g,language_files:w,render_component_path:i}))}catch(d){console.warn("Failed to load translations for dynamic page:",d.message)}return s.status(200).send({data:c,req:n,url:{params:r?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:_})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await l("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,i,a,e={},r={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await l("private/mod/mod-light-plus.min.css","utf-8")||"",i=await p("private/mod/mod-dark-plus.min.css")&&await l("private/mod/mod-dark-plus.min.css","utf-8")||"",a={esm:await p("lib/mod-plus.esm.min.js")&&await l("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await l("lib/mod-plus.iife.min.js","utf-8")||""},e=await k();const n=await b("free"),c=await b("plus");r={...n||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await l("private/mod/mod-light.min.css","utf-8")||"",i=await p("private/mod/mod-dark.min.css")&&await l("private/mod/mod-dark.min.css","utf-8")||"",a={esm:await p("lib/mod.esm.min.js")&&await l("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await l("lib/mod.iife.min.js","utf-8")||""},e=await k(),r={...await b("free")||{}};this.mod={version:s,css:{light:o,dark:i},js:a,globals:e,components:r}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await tt(),await st(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",T),this.express.app.post("/api/_test/queues",(t={},s={})=>C(t,s,this))}register_uploaders(){rt(this.options.uploaders,this)}register_websockets(){nt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await h("../lib/joystickdb_gui/index.js"),i=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!i.routes,has_api:!!i.api,routes_count:Object.keys(i.routes||{}).length,api_structure:i.api?Object.keys(i.api):[]});const a=Object.entries(i.routes||{});for(let n=0;n0&&x(this.express.app,Object.entries(e),this?.options?.api?.context,this?.options?.api?.options),Object.keys(r).length>0&&O(this.express.app,Object.entries(r),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),await this.register_joystickdb_gui(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=pt(this.on_after_start_server,this)}}const F=async(u={})=>{const t=new dt(u);return await t.start(u),t},ut=(u={})=>new Promise(async t=>{if(u?.cluster)_t(async()=>{const s=await F(u);return t(s.express)});else{const s=await F(u);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index 02535116b..f143be9ba 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -77,16 +77,41 @@ class App { } async connect_databases() { - const databases_from_settings = app_settings?.config?.databases; + // Check both app_settings and passed options for database config + const databases_from_settings = app_settings?.config?.databases || this.options?.config?.databases; + + console.log('Connecting databases:', { + databases_from_settings, + databases_count: databases_from_settings?.length || 0, + from_app_settings: !!app_settings?.config?.databases, + from_options: !!this.options?.config?.databases + }); for (let i = 0; i < databases_from_settings?.length; i += 1) { const database_from_settings = databases_from_settings[i]; const database_port = parseInt(process.env.PORT, 10) + 10 + i; const has_multiple_of_provider = (databases_from_settings?.filter((database) => database_from_settings?.provider === database?.provider))?.length > 1; - await register_database(database_from_settings, database_port, has_multiple_of_provider); + console.log(`Registering database ${i + 1}:`, { + name: database_from_settings?.name, + provider: database_from_settings?.provider, + port: database_port, + has_multiple: has_multiple_of_provider + }); + + try { + await register_database(database_from_settings, database_port, has_multiple_of_provider); + console.log(`Database ${database_from_settings?.provider} registered successfully`); + } catch (error) { + console.error(`Failed to register database ${database_from_settings?.provider}:`, error); + } } + console.log('Final process.databases state:', { + databases_exist: !!process.databases, + database_keys: process.databases ? Object.keys(process.databases) : [] + }); + if (databases_from_settings?.length > 0) { const queues_database = get_target_database_connection('queues'); const users_database = get_target_database_connection('users'); @@ -581,14 +606,30 @@ class App { async register_joystickdb_gui() { // NOTE: Automatically register JoystickDB GUI if JoystickDB is detected in databases - const has_joystickdb = process.databases && Object.keys(process.databases).some(key => + const database_keys = process.databases ? Object.keys(process.databases) : []; + const has_joystickdb = database_keys.some(key => key === 'joystickdb' || key.startsWith('joystickdb_') ); + console.log('JoystickDB GUI Detection:', { + database_keys, + has_joystickdb, + process_databases_exists: !!process.databases + }); + if (has_joystickdb) { try { - const joystickdb_gui = await dynamic_import('../lib/joystickdb_gui/index.js'); + console.log('Attempting to load JoystickDB GUI...'); + const joystickdb_gui_module = await dynamic_import('../lib/joystickdb_gui/index.js'); + const joystickdb_gui = joystickdb_gui_module.default || joystickdb_gui_module; + console.log('JoystickDB GUI module loaded:', { + has_routes: !!joystickdb_gui.routes, + has_api: !!joystickdb_gui.api, + routes_count: Object.keys(joystickdb_gui.routes || {}).length, + api_structure: joystickdb_gui.api ? Object.keys(joystickdb_gui.api) : [] + }); + // Register JoystickDB routes const joystickdb_routes = Object.entries(joystickdb_gui.routes || {}); for (let i = 0; i < joystickdb_routes?.length; i += 1) { @@ -596,6 +637,8 @@ class App { const is_object_route = types.is_object(route_handler); const is_function_route = types.is_function(route_handler); + console.log(`Registering route: ${route_path} (${is_function_route ? 'function' : is_object_route ? 'object' : 'unknown'})`); + if (is_function_route) { register_route_from_function(this.express.app, route_path, route_handler); } @@ -606,8 +649,13 @@ class App { } // Register JoystickDB API endpoints - const joystickdb_getters = joystickdb_gui.api.getters || {}; - const joystickdb_setters = joystickdb_gui.api.setters || {}; + const joystickdb_getters = joystickdb_gui.api?.getters || {}; + const joystickdb_setters = joystickdb_gui.api?.setters || {}; + + console.log('Registering API endpoints:', { + getters_count: Object.keys(joystickdb_getters).length, + setters_count: Object.keys(joystickdb_setters).length + }); if (Object.keys(joystickdb_getters).length > 0) { register_getters(this.express.app, Object.entries(joystickdb_getters), this?.options?.api?.context, this?.options?.api?.options); @@ -617,10 +665,13 @@ class App { register_setters(this.express.app, Object.entries(joystickdb_setters), this?.options?.api?.context, this?.options?.api?.options); } - console.log('JoystickDB GUI registered automatically at /joystickdb'); + console.log('JoystickDB GUI registered successfully at /joystickdb'); } catch (error) { - console.warn('Failed to register JoystickDB GUI:', error.message); + console.error('Failed to register JoystickDB GUI:', error); + console.error('Error stack:', error.stack); } + } else { + console.log('JoystickDB not detected in databases, skipping GUI registration'); } } From b01219f7e61af8966016bff10c1b6881bbd85146 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:29:09 -0500 Subject: [PATCH 08/79] release 0.0.0-canary.2278 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 9988fc36f..051adf78f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index e6f3fa127..df15c6aec 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2277", + "canary_version": "0.0.0-canary.2278", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 35d8a638c11fdb5ba9df3131e9968bb610228c9f Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:29:12 -0500 Subject: [PATCH 09/79] release 0.0.0-canary.2278 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index eb100f5cc..dc50d5a6f 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index d30f9a536..0c4a6bffd 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2277", + "canary_version": "0.0.0-canary.2278", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From ca68d4d3c907630800ade5beeda454262432f71c Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:29:15 -0500 Subject: [PATCH 10/79] release 0.0.0-canary.2278 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index fa15601c0..63af985a8 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 0cc3670c0..8ec5faa95 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2277", + "canary_version": "0.0.0-canary.2278", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From 607aeb2912cfda17d5b0c85c05238ea67eacb6d7 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:29:18 -0500 Subject: [PATCH 11/79] release 0.0.0-canary.2278 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 52add0242..5e3f83a8c 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 670ed3206..c2a12f893 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2277", + "canary_version": "0.0.0-canary.2278", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 631bae8299033fcd3745a008586944ff53b628d8 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:29:21 -0500 Subject: [PATCH 12/79] release 0.0.0-canary.2278 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 4caee3601..3c1ce6a0d 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2277", + "version": "0.0.0-canary.2278", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index 7174e2921..13dbf6e37 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2277", + "canary_version": "0.0.0-canary.2278", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 32ac6e64739ae8646a9cc30f59c82a24cbbd4a10 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:57:00 -0500 Subject: [PATCH 13/79] wip debugging joystickdb gui --- node/dist/app/index.js | 2 +- node/dist/lib/joystickdb_gui/routes/index.js | 2 +- node/src/app/index.js | 26 +------------------- node/src/lib/joystickdb_gui/routes/index.js | 9 +++++++ 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/node/dist/app/index.js b/node/dist/app/index.js index 62f4921b7..217966c31 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import q from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import R from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import B from"./api/accounts/recover_password.js";import U from"./api/accounts/reset_password.js";import N from"./api/accounts/signup.js";import W from"./api/accounts/user.js";import A from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import T from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import C from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import H from"./databases/postgresql/create_indexes.js";import V from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import Y from"./generate_machine_id.js";import z from"./generate_process_id.js";import K from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import j from"./databases/get_target_database_connection.js";import Q from"../lib/get_translations.js";import M from"./handle_process_errors.js";import X from"./settings/load.js";import Z from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import tt from"./push/index.js";import st from"./push/logger.js";import et from"./queues/index.js";import b from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import ot from"./register_app_options.js";import it from"./cron_jobs/register.js";import at from"./databases/register_database.js";import x from"./api/register_getters.js";import $ from"./routes/register_route_from_function.js";import v from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import rt from"./uploaders/register.js";import nt from"./websockets/register.js";import ct from"./ssr/index.js";import pt from"./start_express.js";import _t from"./start_node_as_cluster.js";import lt from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:l,readdir:y}=I.promises,D=X();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,M(t?.events),ot(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=D?.config?.databases||this.options?.config?.databases;console.log("Connecting databases:",{databases_from_settings:t,databases_count:t?.length||0,from_app_settings:!!D?.config?.databases,from_options:!!this.options?.config?.databases});for(let s=0;so?.provider===e?.provider)?.length>1;console.log(`Registering database ${s+1}:`,{name:o?.name,provider:o?.provider,port:i,has_multiple:a});try{await at(o,i,a),console.log(`Database ${o?.provider} registered successfully`)}catch(e){console.error(`Failed to register database ${o?.provider}:`,e)}}if(console.log("Final process.databases state:",{databases_exist:!!process.databases,database_keys:process.databases?Object.keys(process.databases):[]}),t?.length>0){const s=j("queues"),o=j("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const i=[s,o],a=i?.filter(r=>r?.provider==="mongodb")?.map(r=>r?.database_type);await L(a);const e=i?.filter(r=>r?.provider==="postgresql")?.map(r=>r?.database_type);await V(e),await H(e)}}async generate_machine_id(){this.joystick_machine_id=await Y()}async generate_process_id(){this.joystick_process_id=await z()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const i=process._joystick_translations[o];if(await p(i.path))try{const a=await y(i.path);i.files=a.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of i.files){const r=`${i.path}/${e}`;try{const n=await h(r);i.cache[e]=n}catch(n){console.warn(`Failed to load translation file: ${r}`,n.message)}}}catch(a){console.warn(`Failed to scan translation directory: ${i.path}`,a.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await y(s),i=o.filter(e=>e.endsWith(".js")&&!e.startsWith("._"));for(const e of i){const r=`${s}/${e}`,n=e.replace(".js","");try{const c=await h(r);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${r}`,c.message)}}const a=o.filter(e=>e.startsWith("base")&&(e.endsWith(".html")||e.endsWith(".css")));for(const e of a){const r=`${s}/${e}`;try{const n=await l(r,"utf-8");process._joystick_email_base_files[e]=n}catch(n){console.warn(`Failed to load email base file: ${r}`,n.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await l("index.html","utf-8"),process._joystick_components={};const s=async a=>{try{const e=await y(a,{withFileTypes:!0});for(const r of e)if(r.isDirectory()){const c=`${`${a}/${r.name}`}/index.js`;if(await p(c))try{const _=await h(c),d=c.replace(`${t}`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(e){console.warn(`Failed to scan directory: ${a}`,e.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const a of o)await p(a)&&await s(a);const i=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(i)){const a=async e=>{try{const r=await y(e,{withFileTypes:!0});for(const n of r)if(n.isDirectory())await a(`${e}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${e}/${n.name}`;try{const _=await h(c),d=c.replace(`${process.cwd()}/`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(r){console.warn(`Failed to scan JoystickDB directory: ${e}`,r.message)}};await a(`${i}/layouts`),await a(`${i}/pages`)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",W),this.express.app.post("/api/_accounts/login",R),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",B),this.express.app.post("/api/_accounts/reset-password",U),this.express.app.post("/api/_accounts/signup",N),this.express.app.get("/api/_accounts/verify-email",A)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,i=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),i,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),i,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){it(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),i=lt(t?.body?.page),a=`${o}/${i}`;if(!t?.body?.page||!await p(a))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${a}.`),s);const e=await h(a);if(e){const r=Z(t?.body?.route_pattern||"",t?.body?.path),n=K({params:r?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await ct({is_dynamic_page_render:!0,component_to_render:e,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let _={};try{const g=`${f()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(_=await Q({req:n,language_files_path:g,language_files:w,render_component_path:i}))}catch(d){console.warn("Failed to load translations for dynamic page:",d.message)}return s.status(200).send({data:c,req:n,url:{params:r?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:_})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await l("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,i,a,e={},r={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await l("private/mod/mod-light-plus.min.css","utf-8")||"",i=await p("private/mod/mod-dark-plus.min.css")&&await l("private/mod/mod-dark-plus.min.css","utf-8")||"",a={esm:await p("lib/mod-plus.esm.min.js")&&await l("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await l("lib/mod-plus.iife.min.js","utf-8")||""},e=await k();const n=await b("free"),c=await b("plus");r={...n||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await l("private/mod/mod-light.min.css","utf-8")||"",i=await p("private/mod/mod-dark.min.css")&&await l("private/mod/mod-dark.min.css","utf-8")||"",a={esm:await p("lib/mod.esm.min.js")&&await l("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await l("lib/mod.iife.min.js","utf-8")||""},e=await k(),r={...await b("free")||{}};this.mod={version:s,css:{light:o,dark:i},js:a,globals:e,components:r}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await tt(),await st(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",T),this.express.app.post("/api/_test/queues",(t={},s={})=>C(t,s,this))}register_uploaders(){rt(this.options.uploaders,this)}register_websockets(){nt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await h("../lib/joystickdb_gui/index.js"),i=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!i.routes,has_api:!!i.api,routes_count:Object.keys(i.routes||{}).length,api_structure:i.api?Object.keys(i.api):[]});const a=Object.entries(i.routes||{});for(let n=0;n0&&x(this.express.app,Object.entries(e),this?.options?.api?.context,this?.options?.api?.options),Object.keys(r).length>0&&O(this.express.app,Object.entries(r),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),await this.register_joystickdb_gui(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=pt(this.on_after_start_server,this)}}const F=async(u={})=>{const t=new dt(u);return await t.start(u),t},ut=(u={})=>new Promise(async t=>{if(u?.cluster)_t(async()=>{const s=await F(u);return t(s.express)});else{const s=await F(u);return t(s.express)}});var _s=ut;export{_s as default}; +import q from"http";import F from"fs";import I from"./api/accounts/authenticated.js";import E from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import W from"./api/accounts/verify_email.js";import A from"./api/test/accounts/delete.js";import S from"./api/test/accounts/signup.js";import T from"./api/test/bootstrap.js";import G from"./api/test/process.js";import P from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import j from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import b from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import $ from"./routes/register_route_from_function.js";import v from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import l from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:m,readdir:y}=F.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;si?.provider===o?.provider)?.length>1;await it(i,e,r)}if(t?.length>0){const s=j("queues"),i=j("users");process.databases._queues=s?.connection,process.databases._users=i?.connection;const e=[s,i],r=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(r);const o=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(o),await L(o)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async i=>{const e=process._joystick_translations[i];if(await p(e.path))try{const r=await y(e.path);e.files=r.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e.files){const a=`${e.path}/${o}`;try{const n=await h(a);e.cache[o]=n}catch(n){console.warn(`Failed to load translation file: ${a}`,n.message)}}}catch(r){console.warn(`Failed to scan translation directory: ${e.path}`,r.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await y(s),e=i.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e){const a=`${s}/${o}`,n=o.replace(".js","");try{const c=await h(a);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const r=i.filter(o=>o.startsWith("base")&&(o.endsWith(".html")||o.endsWith(".css")));for(const o of r){const a=`${s}/${o}`;try{const n=await m(a,"utf-8");process._joystick_email_base_files[o]=n}catch(n){console.warn(`Failed to load email base file: ${a}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${s}`,i.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await m("index.html","utf-8"),process._joystick_components={};const s=async r=>{try{const o=await y(r,{withFileTypes:!0});for(const a of o)if(a.isDirectory()){const c=`${`${r}/${a.name}`}/index.js`;if(await p(c))try{const _=await h(c),d=c.replace(`${t}`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(o){console.warn(`Failed to scan directory: ${r}`,o.message)}},i=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const r of i)await p(r)&&await s(r);const e=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(e)){const r=async o=>{try{const a=await y(o,{withFileTypes:!0});for(const n of a)if(n.isDirectory())await r(`${o}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${o}/${n.name}`;try{const _=await h(c),d=c.replace(`${process.cwd()}/`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(a){console.warn(`Failed to scan JoystickDB directory: ${o}`,a.message)}};await r(`${e}/layouts`),await r(`${e}/pages`)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const i=JSON.parse(s);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",I),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",E),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",W)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,i=this?.options?.api?.options,e=this?.options?.api?.context;t&&l.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,i),s&&l.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,i)}register_caches(){process.caches={},l.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const i=f(),e=_t(t?.body?.page),r=`${i}/${e}`;if(!t?.body?.page||!await p(r))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${r}.`),s);const o=await h(r);if(o){const a=X(t?.body?.route_pattern||"",t?.body?.path),n=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:o,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let _={};try{const g=`${f()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(_=await K({req:n,language_files_path:g,language_files:w,render_component_path:e}))}catch(d){console.warn("Failed to load translations for dynamic page:",d.message)}return s.status(200).send({data:c,req:n,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:_})}return s.status(200).send({})})}register_fixtures(){l.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){l.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await m("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,e,r,o={},a={};if(s==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await m("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await m("private/mod/mod-dark-plus.min.css","utf-8")||"",r={esm:await p("lib/mod-plus.esm.min.js")&&await m("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await m("lib/mod-plus.iife.min.js","utf-8")||""},o=await k();const n=await b("free"),c=await b("plus");a={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await m("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await m("private/mod/mod-dark.min.css","utf-8")||"",r={esm:await p("lib/mod.esm.min.js")&&await m("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await m("lib/mod.iife.min.js","utf-8")||""},o=await k(),a={...await b("free")||{}};this.mod={version:s,css:{light:i,dark:e},js:r,globals:o,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(l.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sT(t,s,this)),this.express.app.get("/api/_test/process",G),this.express.app.delete("/api/_test/accounts",A),this.express.app.post("/api/_test/accounts/signup",S),this.express.app.post("/api/_test/queues",(t={},s={})=>P(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(i=>i==="joystickdb"||i.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const i=await h("../lib/joystickdb_gui/index.js"),e=i.default||i;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const r=Object.entries(e.routes||{});for(let n=0;n0&&x(this.express.app,Object.entries(o),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(i){console.error("Failed to register JoystickDB GUI:",i),console.error("Error stack:",i.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),await this.register_joystickdb_gui(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(u={})=>{const t=new dt(u);return await t.start(u),t},ut=(u={})=>new Promise(async t=>{if(u?.cluster)pt(async()=>{const s=await D(u);return t(s.express)});else{const s=await D(u);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/dist/lib/joystickdb_gui/routes/index.js b/node/dist/lib/joystickdb_gui/routes/index.js index 4fa14783b..85b2bd558 100644 --- a/node/dist/lib/joystickdb_gui/routes/index.js +++ b/node/dist/lib/joystickdb_gui/routes/index.js @@ -1 +1 @@ -import d from"../middleware/auth.js";const e={"/joystickdb":(i={},s={})=>i.session?.joystickdb_user?s.redirect("/joystickdb/dashboard"):s.redirect("/joystickdb/login"),"/joystickdb/login":(i={},s={})=>{if(i.session?.joystickdb_user)return s.redirect("/joystickdb/dashboard");s.render("node/src/lib/joystickdb_gui/ui/pages/login.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{error:i.query.error}})},"/joystickdb/logout":{method:"POST",handler:(i={},s={})=>{i.session.joystickdb_user=null,i.session.joystickdb_client=null,s.redirect("/joystickdb/login")}},"/joystickdb/dashboard":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/dashboard.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{user:i.session.joystickdb_user}})}},"/joystickdb/databases":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/databases.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/databases/:database":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/collections.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database}})}},"/joystickdb/databases/:database/:collection":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/documents.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/query/:database/:collection":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/query.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/admin":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/index.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/users":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/users.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/replication":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/replication.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/stats":{middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/stats.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}}};var o=e;export{o as default}; +import d from"../middleware/auth.js";const e={"/joystickdb":(i={},s={})=>i.session?.joystickdb_user?s.redirect("/joystickdb/dashboard"):s.redirect("/joystickdb/login"),"/joystickdb/login":(i={},s={})=>{if(i.session?.joystickdb_user)return s.redirect("/joystickdb/dashboard");s.render("node/src/lib/joystickdb_gui/ui/pages/login.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{error:i.query.error}})},"/joystickdb/logout":{method:"POST",handler:(i={},s={})=>{i.session.joystickdb_user=null,i.session.joystickdb_client=null,s.redirect("/joystickdb/login")}},"/joystickdb/dashboard":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/dashboard.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{user:i.session.joystickdb_user}})}},"/joystickdb/databases":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/databases.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/databases/:database":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/collections.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database}})}},"/joystickdb/databases/:database/:collection":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/documents.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/query/:database/:collection":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/query.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/admin":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/index.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/users":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/users.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/replication":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/replication.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/stats":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/stats.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}}};var a=e;export{a as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index f143be9ba..023feb1c4 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -80,38 +80,14 @@ class App { // Check both app_settings and passed options for database config const databases_from_settings = app_settings?.config?.databases || this.options?.config?.databases; - console.log('Connecting databases:', { - databases_from_settings, - databases_count: databases_from_settings?.length || 0, - from_app_settings: !!app_settings?.config?.databases, - from_options: !!this.options?.config?.databases - }); - for (let i = 0; i < databases_from_settings?.length; i += 1) { const database_from_settings = databases_from_settings[i]; const database_port = parseInt(process.env.PORT, 10) + 10 + i; const has_multiple_of_provider = (databases_from_settings?.filter((database) => database_from_settings?.provider === database?.provider))?.length > 1; - console.log(`Registering database ${i + 1}:`, { - name: database_from_settings?.name, - provider: database_from_settings?.provider, - port: database_port, - has_multiple: has_multiple_of_provider - }); - - try { - await register_database(database_from_settings, database_port, has_multiple_of_provider); - console.log(`Database ${database_from_settings?.provider} registered successfully`); - } catch (error) { - console.error(`Failed to register database ${database_from_settings?.provider}:`, error); - } + await register_database(database_from_settings, database_port, has_multiple_of_provider); } - console.log('Final process.databases state:', { - databases_exist: !!process.databases, - database_keys: process.databases ? Object.keys(process.databases) : [] - }); - if (databases_from_settings?.length > 0) { const queues_database = get_target_database_connection('queues'); const users_database = get_target_database_connection('users'); diff --git a/node/src/lib/joystickdb_gui/routes/index.js b/node/src/lib/joystickdb_gui/routes/index.js index fc3a8bbd2..bff6d9eee 100644 --- a/node/src/lib/joystickdb_gui/routes/index.js +++ b/node/src/lib/joystickdb_gui/routes/index.js @@ -27,6 +27,7 @@ const joystickdb_routes = { }, }, '/joystickdb/dashboard': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/dashboard.js', { @@ -38,6 +39,7 @@ const joystickdb_routes = { }, }, '/joystickdb/databases': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/databases.js', { @@ -46,6 +48,7 @@ const joystickdb_routes = { }, }, '/joystickdb/databases/:database': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/collections.js', { @@ -57,6 +60,7 @@ const joystickdb_routes = { }, }, '/joystickdb/databases/:database/:collection': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/documents.js', { @@ -69,6 +73,7 @@ const joystickdb_routes = { }, }, '/joystickdb/query/:database/:collection': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/query.js', { @@ -81,6 +86,7 @@ const joystickdb_routes = { }, }, '/joystickdb/admin': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/admin/index.js', { @@ -89,6 +95,7 @@ const joystickdb_routes = { }, }, '/joystickdb/admin/users': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/admin/users.js', { @@ -97,6 +104,7 @@ const joystickdb_routes = { }, }, '/joystickdb/admin/replication': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/admin/replication.js', { @@ -105,6 +113,7 @@ const joystickdb_routes = { }, }, '/joystickdb/admin/stats': { + method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { res.render('node/src/lib/joystickdb_gui/ui/pages/admin/stats.js', { From c7a0c331b8891255a96ca95d2d1fc5743b47d181 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:57:22 -0500 Subject: [PATCH 14/79] release 0.0.0-canary.2279 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 051adf78f..d6e8f3b89 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index df15c6aec..5a849748d 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2278", + "canary_version": "0.0.0-canary.2279", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 5a9889afc4758ef27c30ad1faa46d1b9833e7a27 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:57:31 -0500 Subject: [PATCH 15/79] release 0.0.0-canary.2279 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index dc50d5a6f..261cf072e 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 0c4a6bffd..ef2627155 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2278", + "canary_version": "0.0.0-canary.2279", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 3bc093272c3ef78d52a0708612e9e7f353117fe6 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:57:38 -0500 Subject: [PATCH 16/79] release 0.0.0-canary.2279 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 63af985a8..9ccf274f9 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 8ec5faa95..347cc4235 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2278", + "canary_version": "0.0.0-canary.2279", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From 867d8d6f6b6f8e721d9b040c4d49c8f73b64b15b Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:57:45 -0500 Subject: [PATCH 17/79] release 0.0.0-canary.2279 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 5e3f83a8c..6f85b8d67 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index c2a12f893..ed5c9a557 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2278", + "canary_version": "0.0.0-canary.2279", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 4d754e669f31397d97f02528bf0d99236d8c05a0 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 14:57:50 -0500 Subject: [PATCH 18/79] release 0.0.0-canary.2279 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 3c1ce6a0d..1cf516d31 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2278", + "version": "0.0.0-canary.2279", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index 13dbf6e37..f29fd931f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2278", + "canary_version": "0.0.0-canary.2279", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 1fc5c1fc58538006566a936d8aef59b7c38eaf4b Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:26:35 -0500 Subject: [PATCH 19/79] wip debugging joystickdb gui --- node/dist/app/index.js | 2 +- node/src/app/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/dist/app/index.js b/node/dist/app/index.js index 217966c31..2a1fe335c 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import q from"http";import F from"fs";import I from"./api/accounts/authenticated.js";import E from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import W from"./api/accounts/verify_email.js";import A from"./api/test/accounts/delete.js";import S from"./api/test/accounts/signup.js";import T from"./api/test/bootstrap.js";import G from"./api/test/process.js";import P from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import j from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import b from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import $ from"./routes/register_route_from_function.js";import v from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import l from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:m,readdir:y}=F.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;si?.provider===o?.provider)?.length>1;await it(i,e,r)}if(t?.length>0){const s=j("queues"),i=j("users");process.databases._queues=s?.connection,process.databases._users=i?.connection;const e=[s,i],r=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(r);const o=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(o),await L(o)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async i=>{const e=process._joystick_translations[i];if(await p(e.path))try{const r=await y(e.path);e.files=r.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e.files){const a=`${e.path}/${o}`;try{const n=await h(a);e.cache[o]=n}catch(n){console.warn(`Failed to load translation file: ${a}`,n.message)}}}catch(r){console.warn(`Failed to scan translation directory: ${e.path}`,r.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await y(s),e=i.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e){const a=`${s}/${o}`,n=o.replace(".js","");try{const c=await h(a);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const r=i.filter(o=>o.startsWith("base")&&(o.endsWith(".html")||o.endsWith(".css")));for(const o of r){const a=`${s}/${o}`;try{const n=await m(a,"utf-8");process._joystick_email_base_files[o]=n}catch(n){console.warn(`Failed to load email base file: ${a}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${s}`,i.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await m("index.html","utf-8"),process._joystick_components={};const s=async r=>{try{const o=await y(r,{withFileTypes:!0});for(const a of o)if(a.isDirectory()){const c=`${`${r}/${a.name}`}/index.js`;if(await p(c))try{const _=await h(c),d=c.replace(`${t}`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(o){console.warn(`Failed to scan directory: ${r}`,o.message)}},i=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const r of i)await p(r)&&await s(r);const e=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(e)){const r=async o=>{try{const a=await y(o,{withFileTypes:!0});for(const n of a)if(n.isDirectory())await r(`${o}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${o}/${n.name}`;try{const _=await h(c),d=c.replace(`${process.cwd()}/`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(a){console.warn(`Failed to scan JoystickDB directory: ${o}`,a.message)}};await r(`${e}/layouts`),await r(`${e}/pages`)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const i=JSON.parse(s);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",I),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",E),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",W)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,i=this?.options?.api?.options,e=this?.options?.api?.context;t&&l.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,i),s&&l.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,i)}register_caches(){process.caches={},l.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const i=f(),e=_t(t?.body?.page),r=`${i}/${e}`;if(!t?.body?.page||!await p(r))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${r}.`),s);const o=await h(r);if(o){const a=X(t?.body?.route_pattern||"",t?.body?.path),n=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:o,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let _={};try{const g=`${f()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(_=await K({req:n,language_files_path:g,language_files:w,render_component_path:e}))}catch(d){console.warn("Failed to load translations for dynamic page:",d.message)}return s.status(200).send({data:c,req:n,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:_})}return s.status(200).send({})})}register_fixtures(){l.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){l.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await m("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,e,r,o={},a={};if(s==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await m("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await m("private/mod/mod-dark-plus.min.css","utf-8")||"",r={esm:await p("lib/mod-plus.esm.min.js")&&await m("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await m("lib/mod-plus.iife.min.js","utf-8")||""},o=await k();const n=await b("free"),c=await b("plus");a={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await m("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await m("private/mod/mod-dark.min.css","utf-8")||"",r={esm:await p("lib/mod.esm.min.js")&&await m("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await m("lib/mod.iife.min.js","utf-8")||""},o=await k(),a={...await b("free")||{}};this.mod={version:s,css:{light:i,dark:e},js:r,globals:o,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(l.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sT(t,s,this)),this.express.app.get("/api/_test/process",G),this.express.app.delete("/api/_test/accounts",A),this.express.app.post("/api/_test/accounts/signup",S),this.express.app.post("/api/_test/queues",(t={},s={})=>P(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(i=>i==="joystickdb"||i.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const i=await h("../lib/joystickdb_gui/index.js"),e=i.default||i;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const r=Object.entries(e.routes||{});for(let n=0;n0&&x(this.express.app,Object.entries(o),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(i){console.error("Failed to register JoystickDB GUI:",i),console.error("Error stack:",i.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),this.register_routes(),await this.register_joystickdb_gui(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(u={})=>{const t=new dt(u);return await t.start(u),t},ut=(u={})=>new Promise(async t=>{if(u?.cluster)pt(async()=>{const s=await D(u);return t(s.express)});else{const s=await D(u);return t(s.express)}});var _s=ut;export{_s as default}; +import q from"http";import F from"fs";import I from"./api/accounts/authenticated.js";import E from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import W from"./api/accounts/verify_email.js";import A from"./api/test/accounts/delete.js";import S from"./api/test/accounts/signup.js";import T from"./api/test/bootstrap.js";import G from"./api/test/process.js";import P from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import j from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import b from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import $ from"./routes/register_route_from_function.js";import v from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import l from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:m,readdir:y}=F.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;si?.provider===o?.provider)?.length>1;await it(i,e,r)}if(t?.length>0){const s=j("queues"),i=j("users");process.databases._queues=s?.connection,process.databases._users=i?.connection;const e=[s,i],r=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(r);const o=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(o),await L(o)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async i=>{const e=process._joystick_translations[i];if(await p(e.path))try{const r=await y(e.path);e.files=r.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e.files){const a=`${e.path}/${o}`;try{const n=await h(a);e.cache[o]=n}catch(n){console.warn(`Failed to load translation file: ${a}`,n.message)}}}catch(r){console.warn(`Failed to scan translation directory: ${e.path}`,r.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await y(s),e=i.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e){const a=`${s}/${o}`,n=o.replace(".js","");try{const c=await h(a);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const r=i.filter(o=>o.startsWith("base")&&(o.endsWith(".html")||o.endsWith(".css")));for(const o of r){const a=`${s}/${o}`;try{const n=await m(a,"utf-8");process._joystick_email_base_files[o]=n}catch(n){console.warn(`Failed to load email base file: ${a}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${s}`,i.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await m("index.html","utf-8"),process._joystick_components={};const s=async r=>{try{const o=await y(r,{withFileTypes:!0});for(const a of o)if(a.isDirectory()){const c=`${`${r}/${a.name}`}/index.js`;if(await p(c))try{const _=await h(c),d=c.replace(`${t}`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(o){console.warn(`Failed to scan directory: ${r}`,o.message)}},i=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const r of i)await p(r)&&await s(r);const e=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(e)){const r=async o=>{try{const a=await y(o,{withFileTypes:!0});for(const n of a)if(n.isDirectory())await r(`${o}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${o}/${n.name}`;try{const _=await h(c),d=c.replace(`${process.cwd()}/`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(a){console.warn(`Failed to scan JoystickDB directory: ${o}`,a.message)}};await r(`${e}/layouts`),await r(`${e}/pages`)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const i=JSON.parse(s);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",I),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",E),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",W)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,i=this?.options?.api?.options,e=this?.options?.api?.context;t&&l.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,i),s&&l.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,i)}register_caches(){process.caches={},l.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const i=f(),e=_t(t?.body?.page),r=`${i}/${e}`;if(!t?.body?.page||!await p(r))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${r}.`),s);const o=await h(r);if(o){const a=X(t?.body?.route_pattern||"",t?.body?.path),n=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:o,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let _={};try{const g=`${f()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(_=await K({req:n,language_files_path:g,language_files:w,render_component_path:e}))}catch(d){console.warn("Failed to load translations for dynamic page:",d.message)}return s.status(200).send({data:c,req:n,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:_})}return s.status(200).send({})})}register_fixtures(){l.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){l.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await m("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,e,r,o={},a={};if(s==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await m("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await m("private/mod/mod-dark-plus.min.css","utf-8")||"",r={esm:await p("lib/mod-plus.esm.min.js")&&await m("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await m("lib/mod-plus.iife.min.js","utf-8")||""},o=await k();const n=await b("free"),c=await b("plus");a={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await m("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await m("private/mod/mod-dark.min.css","utf-8")||"",r={esm:await p("lib/mod.esm.min.js")&&await m("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await m("lib/mod.iife.min.js","utf-8")||""},o=await k(),a={...await b("free")||{}};this.mod={version:s,css:{light:i,dark:e},js:r,globals:o,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(l.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sT(t,s,this)),this.express.app.get("/api/_test/process",G),this.express.app.delete("/api/_test/accounts",A),this.express.app.post("/api/_test/accounts/signup",S),this.express.app.post("/api/_test/queues",(t={},s={})=>P(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(i=>i==="joystickdb"||i.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const i=await h("../lib/joystickdb_gui/index.js"),e=i.default||i;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const r=Object.entries(e.routes||{});for(let n=0;n0&&x(this.express.app,Object.entries(o),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(i){console.error("Failed to register JoystickDB GUI:",i),console.error("Error stack:",i.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(u={})=>{const t=new dt(u);return await t.start(u),t},ut=(u={})=>new Promise(async t=>{if(u?.cluster)pt(async()=>{const s=await D(u);return t(s.express)});else{const s=await D(u);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index 023feb1c4..48184dd29 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -672,8 +672,8 @@ class App { this.register_push(); this.register_accounts(); this.register_api(); - this.register_routes(); await this.register_joystickdb_gui(); + this.register_routes(); this.register_dynamic_pages(); this.register_uploaders(); this.register_fixtures(); From b399128249d4b9cf8c7b842115b11deae316a3b4 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:26:42 -0500 Subject: [PATCH 20/79] release 0.0.0-canary.2280 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index d6e8f3b89..cc908dd5f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index 5a849748d..d5606d484 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2279", + "canary_version": "0.0.0-canary.2280", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 55558d6ca0be37bc39250fd4983f4e8d8a48b83d Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:26:45 -0500 Subject: [PATCH 21/79] release 0.0.0-canary.2280 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 261cf072e..369085d6c 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index ef2627155..d8100c939 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2279", + "canary_version": "0.0.0-canary.2280", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 59b01113efc9d8c7801e10de6de66f2ffb7ab835 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:26:48 -0500 Subject: [PATCH 22/79] release 0.0.0-canary.2280 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 9ccf274f9..e6756d243 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 347cc4235..a8a3c9ee2 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2279", + "canary_version": "0.0.0-canary.2280", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From c94c44015531870404b38eda6aafb159fd75e13a Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:26:51 -0500 Subject: [PATCH 23/79] release 0.0.0-canary.2280 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 6f85b8d67..4dc58441a 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index ed5c9a557..04fa7e140 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2279", + "canary_version": "0.0.0-canary.2280", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 20f1604909285bf70d6a4298798a55fd553a663f Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:26:54 -0500 Subject: [PATCH 24/79] release 0.0.0-canary.2280 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 1cf516d31..07451e579 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2279", + "version": "0.0.0-canary.2280", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index f29fd931f..bf1d59401 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2279", + "canary_version": "0.0.0-canary.2280", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From d151631ca0af2ae467b0f976f726c8684ed78b60 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:46:37 -0500 Subject: [PATCH 25/79] wip debugging joystickdb gui --- node/dist/app/index.js | 2 +- node/dist/lib/joystickdb_gui/routes/index.js | 2 +- .../utils/resolve_component_path.js | 1 + node/src/app/index.js | 57 ++++++++++--------- node/src/lib/joystickdb_gui/routes/index.js | 41 ++++++------- .../utils/resolve_component_path.js | 31 ++++++++++ 6 files changed, 84 insertions(+), 50 deletions(-) create mode 100644 node/dist/lib/joystickdb_gui/utils/resolve_component_path.js create mode 100644 node/src/lib/joystickdb_gui/utils/resolve_component_path.js diff --git a/node/dist/app/index.js b/node/dist/app/index.js index 2a1fe335c..6718a4574 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import q from"http";import F from"fs";import I from"./api/accounts/authenticated.js";import E from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import W from"./api/accounts/verify_email.js";import A from"./api/test/accounts/delete.js";import S from"./api/test/accounts/signup.js";import T from"./api/test/bootstrap.js";import G from"./api/test/process.js";import P from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import h from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import j from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import b from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import $ from"./routes/register_route_from_function.js";import v from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import l from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:m,readdir:y}=F.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;si?.provider===o?.provider)?.length>1;await it(i,e,r)}if(t?.length>0){const s=j("queues"),i=j("users");process.databases._queues=s?.connection,process.databases._users=i?.connection;const e=[s,i],r=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(r);const o=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(o),await L(o)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async i=>{const e=process._joystick_translations[i];if(await p(e.path))try{const r=await y(e.path);e.files=r.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e.files){const a=`${e.path}/${o}`;try{const n=await h(a);e.cache[o]=n}catch(n){console.warn(`Failed to load translation file: ${a}`,n.message)}}}catch(r){console.warn(`Failed to scan translation directory: ${e.path}`,r.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const i=await y(s),e=i.filter(o=>o.endsWith(".js")&&!o.startsWith("._"));for(const o of e){const a=`${s}/${o}`,n=o.replace(".js","");try{const c=await h(a);process._joystick_email_templates[n]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const r=i.filter(o=>o.startsWith("base")&&(o.endsWith(".html")||o.endsWith(".css")));for(const o of r){const a=`${s}/${o}`;try{const n=await m(a,"utf-8");process._joystick_email_base_files[o]=n}catch(n){console.warn(`Failed to load email base file: ${a}`,n.message)}}}catch(i){console.warn(`Failed to scan email templates directory: ${s}`,i.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await m("index.html","utf-8"),process._joystick_components={};const s=async r=>{try{const o=await y(r,{withFileTypes:!0});for(const a of o)if(a.isDirectory()){const c=`${`${r}/${a.name}`}/index.js`;if(await p(c))try{const _=await h(c),d=c.replace(`${t}`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load component: ${c}`,_.message)}}}catch(o){console.warn(`Failed to scan directory: ${r}`,o.message)}},i=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const r of i)await p(r)&&await s(r);const e=`${process.cwd()}/node/src/lib/joystickdb_gui/ui`;if(await p(e)){const r=async o=>{try{const a=await y(o,{withFileTypes:!0});for(const n of a)if(n.isDirectory())await r(`${o}/${n.name}`);else if(n.isFile()&&n.name.endsWith(".js")){const c=`${o}/${n.name}`;try{const _=await h(c),d=c.replace(`${process.cwd()}/`,"");process._joystick_components[d]=_}catch(_){console.warn(`Failed to load JoystickDB component: ${c}`,_.message)}}}catch(a){console.warn(`Failed to scan JoystickDB directory: ${o}`,a.message)}};await r(`${e}/layouts`),await r(`${e}/pages`)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const i=JSON.parse(s);["RESTART"].includes(i?.type),i?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",I),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",E),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",W)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,i=this?.options?.api?.options,e=this?.options?.api?.context;t&&l.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,i),s&&l.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,i)}register_caches(){process.caches={},l.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const i=f(),e=_t(t?.body?.page),r=`${i}/${e}`;if(!t?.body?.page||!await p(r))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${r}.`),s);const o=await h(r);if(o){const a=X(t?.body?.route_pattern||"",t?.body?.path),n=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:o,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:n});let _={};try{const g=`${f()}i18n`,w=process._joystick_translations?.normal?.files||[];w.length>0&&(_=await K({req:n,language_files_path:g,language_files:w,render_component_path:e}))}catch(d){console.warn("Failed to load translations for dynamic page:",d.message)}return s.status(200).send({data:c,req:n,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:_})}return s.status(200).send({})})}register_fixtures(){l.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){l.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await m("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let i,e,r,o={},a={};if(s==="plus"){i=await p("private/mod/mod-light-plus.min.css")&&await m("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await m("private/mod/mod-dark-plus.min.css","utf-8")||"",r={esm:await p("lib/mod-plus.esm.min.js")&&await m("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await m("lib/mod-plus.iife.min.js","utf-8")||""},o=await k();const n=await b("free"),c=await b("plus");a={...n||{},...c||{}}}else i=await p("private/mod/mod-light.min.css")&&await m("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await m("private/mod/mod-dark.min.css","utf-8")||"",r={esm:await p("lib/mod.esm.min.js")&&await m("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await m("lib/mod.iife.min.js","utf-8")||""},o=await k(),a={...await b("free")||{}};this.mod={version:s,css:{light:i,dark:e},js:r,globals:o,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(l.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sT(t,s,this)),this.express.app.get("/api/_test/process",G),this.express.app.delete("/api/_test/accounts",A),this.express.app.post("/api/_test/accounts/signup",S),this.express.app.post("/api/_test/queues",(t={},s={})=>P(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(i=>i==="joystickdb"||i.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const i=await h("../lib/joystickdb_gui/index.js"),e=i.default||i;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const r=Object.entries(e.routes||{});for(let n=0;n0&&x(this.express.app,Object.entries(o),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(i){console.error("Failed to register JoystickDB GUI:",i),console.error("Error stack:",i.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(u={})=>{const t=new dt(u);return await t.start(u),t},ut=(u={})=>new Promise(async t=>{if(u?.cluster)pt(async()=>{const s=await D(u);return t(s.express)});else{const s=await D(u);return t(s.express)}});var _s=ut;export{_s as default}; +import D from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import A from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){D.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await L(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("./lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",A)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const q=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await q(d);return t(s.express)});else{const s=await q(d);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/dist/lib/joystickdb_gui/routes/index.js b/node/dist/lib/joystickdb_gui/routes/index.js index 85b2bd558..d2d061871 100644 --- a/node/dist/lib/joystickdb_gui/routes/index.js +++ b/node/dist/lib/joystickdb_gui/routes/index.js @@ -1 +1 @@ -import d from"../middleware/auth.js";const e={"/joystickdb":(i={},s={})=>i.session?.joystickdb_user?s.redirect("/joystickdb/dashboard"):s.redirect("/joystickdb/login"),"/joystickdb/login":(i={},s={})=>{if(i.session?.joystickdb_user)return s.redirect("/joystickdb/dashboard");s.render("node/src/lib/joystickdb_gui/ui/pages/login.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{error:i.query.error}})},"/joystickdb/logout":{method:"POST",handler:(i={},s={})=>{i.session.joystickdb_user=null,i.session.joystickdb_client=null,s.redirect("/joystickdb/login")}},"/joystickdb/dashboard":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/dashboard.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{user:i.session.joystickdb_user}})}},"/joystickdb/databases":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/databases.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/databases/:database":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/collections.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database}})}},"/joystickdb/databases/:database/:collection":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/documents.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/query/:database/:collection":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/query.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js",props:{database:i.params.database,collection:i.params.collection}})}},"/joystickdb/admin":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/index.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/users":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/users.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/replication":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/replication.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}},"/joystickdb/admin/stats":{method:"GET",middleware:[d],handler:(i={},s={})=>{s.render("node/src/lib/joystickdb_gui/ui/pages/admin/stats.js",{layout:"node/src/lib/joystickdb_gui/ui/layouts/main.js"})}}};var a=e;export{a as default}; +import d from"../middleware/auth.js";import s from"../utils/resolve_component_path.js";const o={"/joystickdb":(e={},a={})=>e.session?.joystickdb_user?a.redirect("/joystickdb/dashboard"):a.redirect("/joystickdb/login"),"/joystickdb/login":(e={},a={})=>{if(e.session?.joystickdb_user)return a.redirect("/joystickdb/dashboard");a.render(s("ui/pages/login.js"),{layout:s("ui/layouts/main.js"),props:{error:e.query.error}})},"/joystickdb/logout":{method:"POST",handler:(e={},a={})=>{e.session.joystickdb_user=null,e.session.joystickdb_client=null,a.redirect("/joystickdb/login")}},"/joystickdb/dashboard":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/dashboard.js"),{layout:s("ui/layouts/main.js"),props:{user:e.session.joystickdb_user}})}},"/joystickdb/databases":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/databases.js"),{layout:s("ui/layouts/main.js")})}},"/joystickdb/databases/:database":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/collections.js"),{layout:s("ui/layouts/main.js"),props:{database:e.params.database}})}},"/joystickdb/databases/:database/:collection":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/documents.js"),{layout:s("ui/layouts/main.js"),props:{database:e.params.database,collection:e.params.collection}})}},"/joystickdb/query/:database/:collection":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/query.js"),{layout:s("ui/layouts/main.js"),props:{database:e.params.database,collection:e.params.collection}})}},"/joystickdb/admin":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/admin/index.js"),{layout:s("ui/layouts/main.js")})}},"/joystickdb/admin/users":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/admin/users.js"),{layout:s("ui/layouts/main.js")})}},"/joystickdb/admin/replication":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/admin/replication.js"),{layout:s("ui/layouts/main.js")})}},"/joystickdb/admin/stats":{method:"GET",middleware:[d],handler:(e={},a={})=>{a.render(s("ui/pages/admin/stats.js"),{layout:s("ui/layouts/main.js")})}}};var r=o;export{r as default}; diff --git a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js new file mode 100644 index 000000000..d0b9397b1 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js @@ -0,0 +1 @@ +import e from"path";import{fileURLToPath as n}from"url";const c=n(import.meta.url),_=e.dirname(c),l=(t="")=>{const o=e.resolve(_,".."),r=t.replace(/^\/+/,""),a=e.resolve(o,r);return e.relative(process.cwd(),a)};var m=l;export{m as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index 48184dd29..2ba70ad1d 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -261,38 +261,39 @@ class App { } } - // NOTE: Also scan for JoystickDB GUI components from the source directory - const joystickdb_source_path = `${process.cwd()}/node/src/lib/joystickdb_gui/ui`; - if (await path_exists(joystickdb_source_path)) { - const scan_joystickdb_directory = async (directory_path) => { + // NOTE: Load JoystickDB GUI components using proper path resolution + try { + const resolve_component_path_module = await dynamic_import('./lib/joystickdb_gui/utils/resolve_component_path.js'); + const resolve_component_path = resolve_component_path_module.default || resolve_component_path_module; + + // NOTE: Load the main layout and all page components + const joystickdb_components = [ + 'ui/layouts/main.js', + 'ui/pages/login.js', + 'ui/pages/dashboard.js', + 'ui/pages/databases.js', + 'ui/pages/collections.js', + 'ui/pages/documents.js', + 'ui/pages/query.js', + 'ui/pages/admin/index.js', + 'ui/pages/admin/users.js', + 'ui/pages/admin/replication.js', + 'ui/pages/admin/stats.js' + ]; + + for (const component_path of joystickdb_components) { try { - const entries = await readdir(directory_path, { withFileTypes: true }); + const resolved_path = resolve_component_path(component_path); + const component = await dynamic_import(resolved_path); - for (const entry of entries) { - if (entry.isDirectory()) { - // Recursively scan subdirectories (like admin/) - await scan_joystickdb_directory(`${directory_path}/${entry.name}`); - } else if (entry.isFile() && entry.name.endsWith('.js')) { - // Load direct .js component files - const file_path = `${directory_path}/${entry.name}`; - try { - const component = await dynamic_import(file_path); - // Create relative path that matches the route expectations - const relative_path = file_path.replace(`${process.cwd()}/`, ''); - - process._joystick_components[relative_path] = component; - } catch (error) { - console.warn(`Failed to load JoystickDB component: ${file_path}`, error.message); - } - } - } + // NOTE: Store component with the resolved path as the key for res.render() compatibility + process._joystick_components[resolved_path] = component; } catch (error) { - console.warn(`Failed to scan JoystickDB directory: ${directory_path}`, error.message); + console.warn(`Failed to load JoystickDB component: ${component_path}`, error.message); } - }; - - await scan_joystickdb_directory(`${joystickdb_source_path}/layouts`); - await scan_joystickdb_directory(`${joystickdb_source_path}/pages`); + } + } catch (error) { + console.warn('Failed to load JoystickDB component resolver:', error.message); } } diff --git a/node/src/lib/joystickdb_gui/routes/index.js b/node/src/lib/joystickdb_gui/routes/index.js index bff6d9eee..3504565ca 100644 --- a/node/src/lib/joystickdb_gui/routes/index.js +++ b/node/src/lib/joystickdb_gui/routes/index.js @@ -1,4 +1,5 @@ import joystickdb_auth_middleware from '../middleware/auth.js'; +import resolve_component_path from '../utils/resolve_component_path.js'; const joystickdb_routes = { '/joystickdb': (req = {}, res = {}) => { @@ -11,8 +12,8 @@ const joystickdb_routes = { if (req.session?.joystickdb_user) { return res.redirect('/joystickdb/dashboard'); } - res.render('node/src/lib/joystickdb_gui/ui/pages/login.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/login.js'), { + layout: resolve_component_path('ui/layouts/main.js'), props: { error: req.query.error, }, @@ -30,8 +31,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/dashboard.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/dashboard.js'), { + layout: resolve_component_path('ui/layouts/main.js'), props: { user: req.session.joystickdb_user, }, @@ -42,8 +43,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/databases.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/databases.js'), { + layout: resolve_component_path('ui/layouts/main.js'), }); }, }, @@ -51,8 +52,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/collections.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/collections.js'), { + layout: resolve_component_path('ui/layouts/main.js'), props: { database: req.params.database, }, @@ -63,8 +64,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/documents.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/documents.js'), { + layout: resolve_component_path('ui/layouts/main.js'), props: { database: req.params.database, collection: req.params.collection, @@ -76,8 +77,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/query.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/query.js'), { + layout: resolve_component_path('ui/layouts/main.js'), props: { database: req.params.database, collection: req.params.collection, @@ -89,8 +90,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/admin/index.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/admin/index.js'), { + layout: resolve_component_path('ui/layouts/main.js'), }); }, }, @@ -98,8 +99,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/admin/users.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/admin/users.js'), { + layout: resolve_component_path('ui/layouts/main.js'), }); }, }, @@ -107,8 +108,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/admin/replication.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/admin/replication.js'), { + layout: resolve_component_path('ui/layouts/main.js'), }); }, }, @@ -116,8 +117,8 @@ const joystickdb_routes = { method: 'GET', middleware: [joystickdb_auth_middleware], handler: (req = {}, res = {}) => { - res.render('node/src/lib/joystickdb_gui/ui/pages/admin/stats.js', { - layout: 'node/src/lib/joystickdb_gui/ui/layouts/main.js', + res.render(resolve_component_path('ui/pages/admin/stats.js'), { + layout: resolve_component_path('ui/layouts/main.js'), }); }, }, diff --git a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js new file mode 100644 index 000000000..d03207518 --- /dev/null +++ b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js @@ -0,0 +1,31 @@ +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/* + NOTE: Resolve component paths for JoystickDB GUI components. + This handles both development (source) and production (installed package) scenarios. +*/ + +const resolve_component_path = (relative_component_path = '') => { + // NOTE: Get the base path for JoystickDB GUI components + // In development: /path/to/joystick/node/src/lib/joystickdb_gui + // In production: /path/to/app/node_modules/@joystick.js/node/lib/joystickdb_gui + const base_path = path.resolve(__dirname, '..'); + + // NOTE: Strip any leading path separators from the relative path + const cleaned_relative_path = relative_component_path.replace(/^\/+/, ''); + + // NOTE: Resolve the full component path + const component_path = path.resolve(base_path, cleaned_relative_path); + + // NOTE: Convert to a path format that works with dynamic imports and res.render() + // Make it relative to the current working directory + const relative_to_cwd = path.relative(process.cwd(), component_path); + + return relative_to_cwd; +}; + +export default resolve_component_path; From 6a96c278fdd9fd93fc8afd4495ceb6e6cfd3f0a6 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:46:46 -0500 Subject: [PATCH 26/79] release 0.0.0-canary.2281 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index cc908dd5f..b090cbade 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index d5606d484..2c9b74d7b 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2280", + "canary_version": "0.0.0-canary.2281", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 274268dbcd8b97fe3c3d00cb012dc5fbee6eb685 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:46:48 -0500 Subject: [PATCH 27/79] release 0.0.0-canary.2281 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 369085d6c..55d4bb2fa 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index d8100c939..9d559e60e 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2280", + "canary_version": "0.0.0-canary.2281", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From dbdbb41a968d8d79a09b5d892dfc7a6e0381d730 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:46:51 -0500 Subject: [PATCH 28/79] release 0.0.0-canary.2281 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index e6756d243..7d3b46c9c 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index a8a3c9ee2..0ad16888f 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2280", + "canary_version": "0.0.0-canary.2281", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From 987042f4c4ebb4fd39eb97c24ff56c7b1779ec2b Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:46:54 -0500 Subject: [PATCH 29/79] release 0.0.0-canary.2281 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 4dc58441a..000b378eb 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 04fa7e140..e03841801 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2280", + "canary_version": "0.0.0-canary.2281", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 8364c35d12b3a6d0f00dc6f79a2725b63d3568d2 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 18:46:56 -0500 Subject: [PATCH 30/79] release 0.0.0-canary.2281 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 07451e579..72475827a 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2280", + "version": "0.0.0-canary.2281", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index bf1d59401..6c933106f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2280", + "canary_version": "0.0.0-canary.2281", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 8f634fddfed170996e6c239375bb93fb6a234a28 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:02:27 -0500 Subject: [PATCH 31/79] wip debugging joystickdb gui --- node/dist/app/index.js | 2 +- .../lib/joystickdb_gui/utils/resolve_component_path.js | 2 +- node/src/app/index.js | 8 ++++++++ .../lib/joystickdb_gui/utils/resolve_component_path.js | 9 +++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/node/dist/app/index.js b/node/dist/app/index.js index 6718a4574..42affeabd 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import D from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import A from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){D.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await L(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("./lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",A)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const q=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await q(d);return t(s.express)});else{const s=await q(d);return t(s.express)}});var _s=ut;export{_s as default}; +import q from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import B from"./api/accounts/recover_password.js";import R from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import A from"./api/accounts/user.js";import N from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import C from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await L(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await C(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("./lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c,console.log(`Loaded JoystickDB component: ${a} -> ${r}`,{stored_key:r,component_exists:!!c,total_components:Object.keys(process._joystick_components).length})}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}console.log("All cached components:",Object.keys(process._joystick_components))}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",A),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",B),this.express.app.post("/api/_accounts/reset-password",R),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",N)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await D(d);return t(s.express)});else{const s=await D(d);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js index d0b9397b1..757dfba61 100644 --- a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js +++ b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js @@ -1 +1 @@ -import e from"path";import{fileURLToPath as n}from"url";const c=n(import.meta.url),_=e.dirname(c),l=(t="")=>{const o=e.resolve(_,".."),r=t.replace(/^\/+/,""),a=e.resolve(o,r);return e.relative(process.cwd(),a)};var m=l;export{m as default}; +import e from"path";import{fileURLToPath as a}from"url";const l=a(import.meta.url),s=e.dirname(l),p=(o="")=>{const t=e.resolve(s,".."),n=o.replace(/^\/+/,""),r=e.resolve(t,n),c=e.relative(process.cwd(),r);return console.log("JoystickDB path resolution:",{input:o,base_path:t,component_path:r,relative_to_cwd:c,__dirname:s,cwd:process.cwd()}),c};var _=p;export{_ as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index 2ba70ad1d..d5f961396 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -288,10 +288,18 @@ class App { // NOTE: Store component with the resolved path as the key for res.render() compatibility process._joystick_components[resolved_path] = component; + + console.log(`Loaded JoystickDB component: ${component_path} -> ${resolved_path}`, { + stored_key: resolved_path, + component_exists: !!component, + total_components: Object.keys(process._joystick_components).length + }); } catch (error) { console.warn(`Failed to load JoystickDB component: ${component_path}`, error.message); } } + + console.log('All cached components:', Object.keys(process._joystick_components)); } catch (error) { console.warn('Failed to load JoystickDB component resolver:', error.message); } diff --git a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js index d03207518..669013632 100644 --- a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js +++ b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js @@ -25,6 +25,15 @@ const resolve_component_path = (relative_component_path = '') => { // Make it relative to the current working directory const relative_to_cwd = path.relative(process.cwd(), component_path); + console.log(`JoystickDB path resolution:`, { + input: relative_component_path, + base_path, + component_path, + relative_to_cwd, + __dirname, + cwd: process.cwd() + }); + return relative_to_cwd; }; From 70a35f7cfa4b05fdead536ed43edcbf840d6f28f Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:02:34 -0500 Subject: [PATCH 32/79] release 0.0.0-canary.2282 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index b090cbade..1e30b9931 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index 2c9b74d7b..4cadcf2d1 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2281", + "canary_version": "0.0.0-canary.2282", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 651e41d2a9fa661bd85c599caabdb5cdd815cb9b Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:02:37 -0500 Subject: [PATCH 33/79] release 0.0.0-canary.2282 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 55d4bb2fa..32553ede5 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 9d559e60e..2da2e82d1 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2281", + "canary_version": "0.0.0-canary.2282", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 6bfc8d52e62051db23278357c76c8eda63059f2c Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:02:40 -0500 Subject: [PATCH 34/79] release 0.0.0-canary.2282 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 7d3b46c9c..3e8843907 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 0ad16888f..63a73b924 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2281", + "canary_version": "0.0.0-canary.2282", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From 898585d652fb4ca799d558188e122426723d6421 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:02:43 -0500 Subject: [PATCH 35/79] release 0.0.0-canary.2282 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 000b378eb..f5989fded 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index e03841801..3f4ba5fe4 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2281", + "canary_version": "0.0.0-canary.2282", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 13a5962687de9a08699ad9bc6d979f8eb22bc555 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:02:45 -0500 Subject: [PATCH 36/79] release 0.0.0-canary.2282 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 72475827a..e1a2ffa09 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2281", + "version": "0.0.0-canary.2282", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index 6c933106f..d65ac8413 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2281", + "canary_version": "0.0.0-canary.2282", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 5a5ad5bace067bda0ff03c5e95d940d0f3f0517c Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:10:27 -0500 Subject: [PATCH 37/79] wip debugging joystickdb gui --- node/dist/app/index.js | 2 +- node/src/app/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node/dist/app/index.js b/node/dist/app/index.js index 42affeabd..cd1dae1a2 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import q from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import B from"./api/accounts/recover_password.js";import R from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import A from"./api/accounts/user.js";import N from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import C from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await L(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await C(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("./lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c,console.log(`Loaded JoystickDB component: ${a} -> ${r}`,{stored_key:r,component_exists:!!c,total_components:Object.keys(process._joystick_components).length})}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}console.log("All cached components:",Object.keys(process._joystick_components))}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",A),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",B),this.express.app.post("/api/_accounts/reset-password",R),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",N)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await D(d);return t(s.express)});else{const s=await D(d);return t(s.express)}});var _s=ut;export{_s as default}; +import q from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import B from"./api/accounts/recover_password.js";import R from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import A from"./api/accounts/user.js";import N from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import C from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await L(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await C(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("../lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c,console.log(`Loaded JoystickDB component: ${a} -> ${r}`,{stored_key:r,component_exists:!!c,total_components:Object.keys(process._joystick_components).length})}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}console.log("All cached components:",Object.keys(process._joystick_components))}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",A),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",B),this.express.app.post("/api/_accounts/reset-password",R),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",N)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await D(d);return t(s.express)});else{const s=await D(d);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index d5f961396..e5803cdab 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -263,7 +263,7 @@ class App { // NOTE: Load JoystickDB GUI components using proper path resolution try { - const resolve_component_path_module = await dynamic_import('./lib/joystickdb_gui/utils/resolve_component_path.js'); + const resolve_component_path_module = await dynamic_import('../lib/joystickdb_gui/utils/resolve_component_path.js'); const resolve_component_path = resolve_component_path_module.default || resolve_component_path_module; // NOTE: Load the main layout and all page components From 0bf839b157e4d71af58b9a4495b37d7a0d59e648 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:10:34 -0500 Subject: [PATCH 38/79] release 0.0.0-canary.2283 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 1e30b9931..cff0623a9 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index 4cadcf2d1..b01953642 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2282", + "canary_version": "0.0.0-canary.2283", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 7a28563849241ed4c8efe4804626887533838109 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:10:37 -0500 Subject: [PATCH 39/79] release 0.0.0-canary.2283 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 32553ede5..ac87a1b51 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 2da2e82d1..b62f9cc51 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2282", + "canary_version": "0.0.0-canary.2283", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 79349fc0b7f7ff900d689357a845a4876e10859c Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:10:40 -0500 Subject: [PATCH 40/79] release 0.0.0-canary.2283 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 3e8843907..eefb78b84 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 63a73b924..1132f54b2 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2282", + "canary_version": "0.0.0-canary.2283", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From f08abeab939f28554560edbc7212919dc739d487 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:10:43 -0500 Subject: [PATCH 41/79] release 0.0.0-canary.2283 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index f5989fded..3c6b9a069 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 3f4ba5fe4..3e628053c 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2282", + "canary_version": "0.0.0-canary.2283", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From b3e38cd23323b3e93472bae1fdb7bf8efd8370c5 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:10:46 -0500 Subject: [PATCH 42/79] release 0.0.0-canary.2283 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index e1a2ffa09..8eaa8cf86 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2282", + "version": "0.0.0-canary.2283", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index d65ac8413..f19696c07 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2282", + "canary_version": "0.0.0-canary.2283", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From bc19a5e63ade6481f299163d65d03b1901d8e979 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:18:42 -0500 Subject: [PATCH 43/79] wip debugging joystickdb gui --- .../lib/joystickdb_gui/utils/resolve_component_path.js | 2 +- .../lib/joystickdb_gui/utils/resolve_component_path.js | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js index 757dfba61..6d5e51fc1 100644 --- a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js +++ b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js @@ -1 +1 @@ -import e from"path";import{fileURLToPath as a}from"url";const l=a(import.meta.url),s=e.dirname(l),p=(o="")=>{const t=e.resolve(s,".."),n=o.replace(/^\/+/,""),r=e.resolve(t,n),c=e.relative(process.cwd(),r);return console.log("JoystickDB path resolution:",{input:o,base_path:t,component_path:r,relative_to_cwd:c,__dirname:s,cwd:process.cwd()}),c};var _=p;export{_ as default}; +import o from"path";import{fileURLToPath as p}from"url";const i=p(import.meta.url),c=o.dirname(i),_=(s="")=>{const n=o.resolve(c,".."),l=s.replace(/^\/+/,""),t=o.resolve(n,l),e=o.relative(process.cwd(),t),r=e.startsWith("node_modules/"),a=r?t:e;return console.log("JoystickDB path resolution:",{input:s,base_path:n,component_path:t,relative_to_cwd:e,should_use_absolute:r,final_path:a,__dirname:c,cwd:process.cwd()}),a};var h=_;export{h as default}; diff --git a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js index 669013632..7274bc47e 100644 --- a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js +++ b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js @@ -21,20 +21,24 @@ const resolve_component_path = (relative_component_path = '') => { // NOTE: Resolve the full component path const component_path = path.resolve(base_path, cleaned_relative_path); - // NOTE: Convert to a path format that works with dynamic imports and res.render() - // Make it relative to the current working directory + // NOTE: Use absolute path for dynamic imports to avoid package name confusion + // When relative_to_cwd starts with 'node_modules/', Node.js thinks it's a package const relative_to_cwd = path.relative(process.cwd(), component_path); + const should_use_absolute = relative_to_cwd.startsWith('node_modules/'); + const final_path = should_use_absolute ? component_path : relative_to_cwd; console.log(`JoystickDB path resolution:`, { input: relative_component_path, base_path, component_path, relative_to_cwd, + should_use_absolute, + final_path, __dirname, cwd: process.cwd() }); - return relative_to_cwd; + return final_path; }; export default resolve_component_path; From 70cf6182d13820c00be1c83729e16e8744ce4b67 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:18:50 -0500 Subject: [PATCH 44/79] release 0.0.0-canary.2284 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index cff0623a9..528946e11 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index b01953642..6c8f9c7c9 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2283", + "canary_version": "0.0.0-canary.2284", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 76901e5a40fac880e0d5b3ea765a8ca3749d263d Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:18:53 -0500 Subject: [PATCH 45/79] release 0.0.0-canary.2284 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index ac87a1b51..f9da8fac2 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index b62f9cc51..d493e74b2 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2283", + "canary_version": "0.0.0-canary.2284", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 876e929a185aec8cb0f44fa993fa8a0e88bacc20 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:18:56 -0500 Subject: [PATCH 46/79] release 0.0.0-canary.2284 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index eefb78b84..35d4eada6 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 1132f54b2..adb01e5b1 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2283", + "canary_version": "0.0.0-canary.2284", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From e2f3200594f4cdfab9004064f60212375cd24fee Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:18:59 -0500 Subject: [PATCH 47/79] release 0.0.0-canary.2284 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 3c6b9a069..5c803f983 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 3e628053c..7175559aa 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2283", + "canary_version": "0.0.0-canary.2284", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 2eaf463617ae4b3b65b93dfefecf3556ee44e161 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:19:01 -0500 Subject: [PATCH 48/79] release 0.0.0-canary.2284 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 8eaa8cf86..fb79b96db 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2283", + "version": "0.0.0-canary.2284", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index f19696c07..fe907772b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2283", + "canary_version": "0.0.0-canary.2284", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 86e9a426bd3dc10c3c43c0dd8b01f51baae672e3 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:34:51 -0500 Subject: [PATCH 49/79] wip debugging joystickdb gui --- node/dist/app/middleware/render/index.js | 2 +- node/src/app/middleware/render/index.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/node/dist/app/middleware/render/index.js b/node/dist/app/middleware/render/index.js index b871e37ee..08ac82917 100644 --- a/node/dist/app/middleware/render/index.js +++ b/node/dist/app/middleware/render/index.js @@ -1 +1 @@ -import k from"fs";import i from"../generate_joystick_error_page.js";import j from"../../../lib/get_joystick_build_path.js";import b from"../../../lib/get_translations.js";import $ from"./get_url.js";import w from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:E}=k.promises,z=j(),A=(o,s,_,e={})=>{s.render=async(l="",t={})=>{const m=u(l),c=u(t?.layout),n=m,a=t?.layout?c:null;if(!process._joystick_components[n])return s.status(404).send(i({type:"page_not_found",path:`res.render('${n}')`,frame:null,stack:`A page component at the path ${n} could not be found.`}));if(a&&!process._joystick_components[a])return s.status(404).send(i({type:"layout_not_found",path:`res.render('${n}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const r=process._joystick_components[n],h=a?process._joystick_components[a]:null,p={...t?.props||{},theme:o?.cookies?.theme||"light"};a&&(p.page=r);const d=process._joystick_html,f=await b({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:l,req:o}),y=$(o),g=await w({api_schema:e?.options?.api,attributes:t?.attributes,base_html:d,component_options:{props:p,translations:f,url:y},component_to_render:h||r,escaping:t?.escaping,head:t?.head,render_component_path:m,render_layout_path:c,req:o,res:s,mod:t?.mod===!1?null:{in_use:!!e?.mod,css:e?.mod?.css||null,js:e?.mod?.js||null,theme:o?.cookies?.theme||e?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return s.status(200).send(g)},_()};var G=A;export{G as default}; +import j from"fs";import r from"../generate_joystick_error_page.js";import b from"../../../lib/get_joystick_build_path.js";import $ from"../../../lib/get_translations.js";import w from"./get_url.js";import A from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:E}=j.promises,z=b(),C=(s,e,_,n={})=>{e.render=async(l="",t={})=>{const m=u(l),c=u(t?.layout),o=m,a=t?.layout?c:null;if(!process._joystick_components[o])return console.log(`Component lookup failed for: ${o}`),console.log("Available components:",Object.keys(process._joystick_components).filter(k=>k.includes("joystickdb"))),e.status(404).send(r({type:"page_not_found",path:`res.render('${o}')`,frame:null,stack:`A page component at the path ${o} could not be found.`}));if(a&&!process._joystick_components[a])return e.status(404).send(r({type:"layout_not_found",path:`res.render('${o}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const i=process._joystick_components[o],d=a?process._joystick_components[a]:null,p={...t?.props||{},theme:s?.cookies?.theme||"light"};a&&(p.page=i);const h=process._joystick_html,f=await $({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:l,req:s}),y=w(s),g=await A({api_schema:n?.options?.api,attributes:t?.attributes,base_html:h,component_options:{props:p,translations:f,url:y},component_to_render:d||i,escaping:t?.escaping,head:t?.head,render_component_path:m,render_layout_path:c,req:s,res:e,mod:t?.mod===!1?null:{in_use:!!n?.mod,css:n?.mod?.css||null,js:n?.mod?.js||null,theme:s?.cookies?.theme||n?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return e.status(200).send(g)},_()};var G=C;export{G as default}; diff --git a/node/src/app/middleware/render/index.js b/node/src/app/middleware/render/index.js index 9201fd6b1..68c4bfc2b 100644 --- a/node/src/app/middleware/render/index.js +++ b/node/src/app/middleware/render/index.js @@ -22,6 +22,9 @@ const render_middleware = (req, res, next, app_instance = {}) => { const layout_path = render_options?.layout ? sanitized_render_layout_path : null; if (!process._joystick_components[component_path]) { + console.log(`Component lookup failed for: ${component_path}`); + console.log(`Available components:`, Object.keys(process._joystick_components).filter(key => key.includes('joystickdb'))); + return res.status(404).send( generate_joystick_error_page({ type: 'page_not_found', From 774e269d111e4628a049a9290bf1adb557fa3294 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:34:58 -0500 Subject: [PATCH 50/79] release 0.0.0-canary.2285 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 528946e11..a2c8f48ae 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index 6c8f9c7c9..78499f180 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2284", + "canary_version": "0.0.0-canary.2285", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From a43840065ea0f7ae94cbc8c8257b03c7437968d5 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:35:01 -0500 Subject: [PATCH 51/79] release 0.0.0-canary.2285 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index f9da8fac2..cdc36b67d 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index d493e74b2..17e09b175 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2284", + "canary_version": "0.0.0-canary.2285", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 44cf6fea64a55fbcc4798082aa7a8438d905f0a5 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:35:04 -0500 Subject: [PATCH 52/79] release 0.0.0-canary.2285 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 35d4eada6..3f036c2f2 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index adb01e5b1..af7f525f3 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2284", + "canary_version": "0.0.0-canary.2285", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From dfec16a4c154dd5d5d245f0975a1246a00069cfd Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:35:07 -0500 Subject: [PATCH 53/79] release 0.0.0-canary.2285 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 5c803f983..ad0ac38f4 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 7175559aa..ba9908e15 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2284", + "canary_version": "0.0.0-canary.2285", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 865219a05401200028a30680f005e09a3546e9c4 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Fri, 3 Oct 2025 19:35:11 -0500 Subject: [PATCH 54/79] release 0.0.0-canary.2285 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index fb79b96db..7e4e91e6d 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2284", + "version": "0.0.0-canary.2285", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index fe907772b..e8cb44ab0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2284", + "canary_version": "0.0.0-canary.2285", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 012a91eda55e4d490e86e39667c817586ccc8ef6 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 13:05:48 -0500 Subject: [PATCH 55/79] wip debugging joystickdb routes --- node/dist/app/middleware/render/index.js | 2 +- node/src/app/middleware/render/index.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/node/dist/app/middleware/render/index.js b/node/dist/app/middleware/render/index.js index 08ac82917..35a002d8e 100644 --- a/node/dist/app/middleware/render/index.js +++ b/node/dist/app/middleware/render/index.js @@ -1 +1 @@ -import j from"fs";import r from"../generate_joystick_error_page.js";import b from"../../../lib/get_joystick_build_path.js";import $ from"../../../lib/get_translations.js";import w from"./get_url.js";import A from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:E}=j.promises,z=b(),C=(s,e,_,n={})=>{e.render=async(l="",t={})=>{const m=u(l),c=u(t?.layout),o=m,a=t?.layout?c:null;if(!process._joystick_components[o])return console.log(`Component lookup failed for: ${o}`),console.log("Available components:",Object.keys(process._joystick_components).filter(k=>k.includes("joystickdb"))),e.status(404).send(r({type:"page_not_found",path:`res.render('${o}')`,frame:null,stack:`A page component at the path ${o} could not be found.`}));if(a&&!process._joystick_components[a])return e.status(404).send(r({type:"layout_not_found",path:`res.render('${o}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const i=process._joystick_components[o],d=a?process._joystick_components[a]:null,p={...t?.props||{},theme:s?.cookies?.theme||"light"};a&&(p.page=i);const h=process._joystick_html,f=await $({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:l,req:s}),y=w(s),g=await A({api_schema:n?.options?.api,attributes:t?.attributes,base_html:h,component_options:{props:p,translations:f,url:y},component_to_render:d||i,escaping:t?.escaping,head:t?.head,render_component_path:m,render_layout_path:c,req:s,res:e,mod:t?.mod===!1?null:{in_use:!!n?.mod,css:n?.mod?.css||null,js:n?.mod?.js||null,theme:s?.cookies?.theme||n?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return e.status(200).send(g)},_()};var G=C;export{G as default}; +import j from"fs";import r from"../generate_joystick_error_page.js";import b from"../../../lib/get_joystick_build_path.js";import $ from"../../../lib/get_translations.js";import w from"./get_url.js";import A from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:O}=j.promises,z=b(),C=(s,e,_,n={})=>{e.render=async(c="",o={})=>{console.log(`res.render() called with path: "${c}"`);const i=u(c),l=u(o?.layout),t=i,a=o?.layout?l:null;if(console.log(`After sanitization: "${t}"`),console.log(`Component exists in cache: ${!!process._joystick_components[t]}`),!process._joystick_components[t])return console.log(`Component lookup failed for: ${t}`),console.log("Available JoystickDB components:",Object.keys(process._joystick_components).filter(k=>k.includes("joystickdb"))),e.status(404).send(r({type:"page_not_found",path:`res.render('${t}')`,frame:null,stack:`A page component at the path ${t} could not be found.`}));if(a&&!process._joystick_components[a])return e.status(404).send(r({type:"layout_not_found",path:`res.render('${t}', { layout: '${l}' })`,frame:null,stack:`A layout component at the path ${o?.layout} could not be found.`}));const m=process._joystick_components[t],d=a?process._joystick_components[a]:null,p={...o?.props||{},theme:s?.cookies?.theme||"light"};a&&(p.page=m);const h=process._joystick_html,y=await $({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:c,req:s}),f=w(s),g=await A({api_schema:n?.options?.api,attributes:o?.attributes,base_html:h,component_options:{props:p,translations:y,url:f},component_to_render:d||m,escaping:o?.escaping,head:o?.head,render_component_path:i,render_layout_path:l,req:s,res:e,mod:o?.mod===!1?null:{in_use:!!n?.mod,css:n?.mod?.css||null,js:n?.mod?.js||null,theme:s?.cookies?.theme||n?.options?.mod?.default_theme||"light",components_in_use:o?.mod?.components}});return e.status(200).send(g)},_()};var E=C;export{E as default}; diff --git a/node/src/app/middleware/render/index.js b/node/src/app/middleware/render/index.js index 68c4bfc2b..e82e3cf02 100644 --- a/node/src/app/middleware/render/index.js +++ b/node/src/app/middleware/render/index.js @@ -14,6 +14,8 @@ const render_middleware = (req, res, next, app_instance = {}) => { // NOTE: Set res.render here so we have access to req, res, and // app_instance objects inside of the definition. res.render = async (render_component_path = '', render_options = {}) => { + console.log(`res.render() called with path: "${render_component_path}"`); + // NOTE: Safety mechanism. Don't punish a developer if the path they pass to res.render() // has a forward slash prepended, just strip it for them. const sanitized_render_component_path = strip_preceeding_slash(render_component_path); @@ -21,9 +23,12 @@ const render_middleware = (req, res, next, app_instance = {}) => { const component_path = sanitized_render_component_path; const layout_path = render_options?.layout ? sanitized_render_layout_path : null; + console.log(`After sanitization: "${component_path}"`); + console.log(`Component exists in cache: ${!!process._joystick_components[component_path]}`); + if (!process._joystick_components[component_path]) { console.log(`Component lookup failed for: ${component_path}`); - console.log(`Available components:`, Object.keys(process._joystick_components).filter(key => key.includes('joystickdb'))); + console.log(`Available JoystickDB components:`, Object.keys(process._joystick_components).filter(key => key.includes('joystickdb'))); return res.status(404).send( generate_joystick_error_page({ From 73df20bf7983f29d3b0cfd72801a296a936bf02b Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 13:06:00 -0500 Subject: [PATCH 56/79] release 0.0.0-canary.2286 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index a2c8f48ae..a0ebbf6bd 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index 78499f180..70ad0ee73 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2285", + "canary_version": "0.0.0-canary.2286", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From fdec389d9a7c6cc0352a8471ab4040daf33dbeb3 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 13:06:03 -0500 Subject: [PATCH 57/79] release 0.0.0-canary.2286 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index cdc36b67d..ec6818408 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 17e09b175..48a7f1d5f 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2285", + "canary_version": "0.0.0-canary.2286", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 97ee665608f53d4ffd7c0f5895e8057603bb9deb Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 13:06:06 -0500 Subject: [PATCH 58/79] release 0.0.0-canary.2286 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 3f036c2f2..1b47452d9 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index af7f525f3..56383d6e1 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2285", + "canary_version": "0.0.0-canary.2286", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From 481bc6c21f2c09222a740a9d89ca386e9743fcc0 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 13:06:09 -0500 Subject: [PATCH 59/79] release 0.0.0-canary.2286 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index ad0ac38f4..578704cf0 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index ba9908e15..faafec733 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2285", + "canary_version": "0.0.0-canary.2286", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From dfe0de6dfabed7e9355ccf158f87d4dc9cc579ce Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 13:06:11 -0500 Subject: [PATCH 60/79] release 0.0.0-canary.2286 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 7e4e91e6d..1063f51dd 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2285", + "version": "0.0.0-canary.2286", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index e8cb44ab0..1b4591939 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2285", + "canary_version": "0.0.0-canary.2286", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From e47b3d4333e57d63bf801f54200389d0e610d25d Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 15:47:11 -0500 Subject: [PATCH 61/79] wip debugging joystickdb gui --- node/dist/app/index.js | 2 +- node/dist/app/middleware/render/index.js | 2 +- .../joystickdb_gui/ui/pages/admin/index.js | 123 ++++++ .../ui/pages/admin/replication.js | 226 +++++++++++ .../joystickdb_gui/ui/pages/admin/stats.js | 295 ++++++++++++++ .../joystickdb_gui/ui/pages/admin/users.js | 175 ++++++++ .../joystickdb_gui/ui/pages/collections.js | 90 +++++ .../lib/joystickdb_gui/ui/pages/databases.js | 59 +++ .../lib/joystickdb_gui/ui/pages/documents.js | 212 ++++++++++ .../dist/lib/joystickdb_gui/ui/pages/query.js | 220 +++++++++++ .../utils/resolve_component_path.js | 2 +- node/src/app/index.js | 6 - node/src/app/middleware/render/index.js | 8 - .../joystickdb_gui/ui/pages/admin/index.js | 145 +++++++ .../ui/pages/admin/replication.js | 258 ++++++++++++ .../joystickdb_gui/ui/pages/admin/stats.js | 374 ++++++++++++++++++ .../joystickdb_gui/ui/pages/admin/users.js | 209 ++++++++++ .../joystickdb_gui/ui/pages/collections.js | 116 ++++++ .../lib/joystickdb_gui/ui/pages/databases.js | 79 ++++ .../lib/joystickdb_gui/ui/pages/documents.js | 255 ++++++++++++ node/src/lib/joystickdb_gui/ui/pages/query.js | 304 ++++++++++++++ .../utils/resolve_component_path.js | 11 - 22 files changed, 3143 insertions(+), 28 deletions(-) create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/admin/index.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/admin/users.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/collections.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/databases.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/documents.js create mode 100644 node/dist/lib/joystickdb_gui/ui/pages/query.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/admin/index.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/admin/replication.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/admin/stats.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/admin/users.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/collections.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/databases.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/documents.js create mode 100644 node/src/lib/joystickdb_gui/ui/pages/query.js diff --git a/node/dist/app/index.js b/node/dist/app/index.js index cd1dae1a2..bb4598b76 100644 --- a/node/dist/app/index.js +++ b/node/dist/app/index.js @@ -1 +1 @@ -import q from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import B from"./api/accounts/recover_password.js";import R from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import A from"./api/accounts/user.js";import N from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import L from"./databases/mongodb/create_indexes.js";import C from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){q.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await L(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await C(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("../lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c,console.log(`Loaded JoystickDB component: ${a} -> ${r}`,{stored_key:r,component_exists:!!c,total_components:Object.keys(process._joystick_components).length})}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}console.log("All cached components:",Object.keys(process._joystick_components))}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",A),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",B),this.express.app.post("/api/_accounts/reset-password",R),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",N)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const D=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await D(d);return t(s.express)});else{const s=await D(d);return t(s.express)}});var _s=ut;export{_s as default}; +import D from"http";import I from"fs";import E from"./api/accounts/authenticated.js";import F from"./api/accounts/login.js";import J from"./api/accounts/logout.js";import R from"./api/accounts/recover_password.js";import B from"./api/accounts/reset_password.js";import U from"./api/accounts/signup.js";import N from"./api/accounts/user.js";import A from"./api/accounts/verify_email.js";import S from"./api/test/accounts/delete.js";import W from"./api/test/accounts/signup.js";import G from"./api/test/bootstrap.js";import P from"./api/test/process.js";import T from"./api/test/queues.js";import C from"./databases/mongodb/create_indexes.js";import L from"./databases/postgresql/create_indexes.js";import H from"./databases/postgresql/create_tables.js";import u from"../lib/dynamic_import.js";import V from"./generate_machine_id.js";import Y from"./generate_process_id.js";import z from"../lib/get_browser_safe_request.js";import f from"../lib/get_joystick_build_path.js";import w from"./databases/get_target_database_connection.js";import K from"../lib/get_translations.js";import Q from"./handle_process_errors.js";import M from"./settings/load.js";import X from"../lib/parse_route_pattern.js";import p from"../lib/path_exists.js";import Z from"./push/index.js";import tt from"./push/logger.js";import st from"./queues/index.js";import y from"../lib/read_mod_component_css.js";import k from"../lib/read_mod_global_css.js";import et from"./register_app_options.js";import ot from"./cron_jobs/register.js";import it from"./databases/register_database.js";import x from"./api/register_getters.js";import v from"./routes/register_route_from_function.js";import $ from"./routes/register_route_from_object.js";import O from"./api/register_setters.js";import at from"./uploaders/register.js";import rt from"./websockets/register.js";import nt from"./ssr/index.js";import ct from"./start_express.js";import pt from"./start_node_as_cluster.js";import _t from"../lib/strip_preceeding_slash.js";import m from"../lib/types.js";import mt from"../lib/websocket_client.js";const{readFile:_,readdir:b}=I.promises,lt=M();class dt{constructor(t={}){D.globalAgent.maxSockets=1/0,Q(t?.events),et(this,t),this.generate_machine_id(),this.generate_process_id(),process.title=process.env.NODE_ENV==="test"?"joystick_test_app":"joystick_app",process.joystick={app_options:t,external_process_ids:[],track_external_process:(s="")=>{process.send({external_process_id:s}),process.joystick.external_process_ids.push(s)}}}async connect_databases(){const t=lt?.config?.databases||this.options?.config?.databases;for(let s=0;so?.provider===i?.provider)?.length>1;await it(o,e,n)}if(t?.length>0){const s=w("queues"),o=w("users");process.databases._queues=s?.connection,process.databases._users=o?.connection;const e=[s,o],n=e?.filter(a=>a?.provider==="mongodb")?.map(a=>a?.database_type);await C(n);const i=e?.filter(a=>a?.provider==="postgresql")?.map(a=>a?.database_type);await H(i),await L(i)}}async generate_machine_id(){this.joystick_machine_id=await V()}async generate_process_id(){this.joystick_process_id=await Y()}async load_translations(){const t=f();if(!await p(t))return;process._joystick_translations={normal:{files:[],path:`${t}i18n`,cache:{}},email:{files:[],path:`${t}i18n/email`,cache:{}}};const s=async o=>{const e=process._joystick_translations[o];if(await p(e.path))try{const n=await b(e.path);e.files=n.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e.files){const a=`${e.path}/${i}`;try{const r=await u(a);e.cache[i]=r}catch(r){console.warn(`Failed to load translation file: ${a}`,r.message)}}}catch(n){console.warn(`Failed to scan translation directory: ${e.path}`,n.message)}};await s("normal"),await s("email")}async load_email_templates(){const s=`${f()}email`;if(await p(s)){process._joystick_email_templates={},process._joystick_email_base_files={};try{const o=await b(s),e=o.filter(i=>i.endsWith(".js")&&!i.startsWith("._"));for(const i of e){const a=`${s}/${i}`,r=i.replace(".js","");try{const c=await u(a);process._joystick_email_templates[r]=c}catch(c){console.warn(`Failed to load email template: ${a}`,c.message)}}const n=o.filter(i=>i.startsWith("base")&&(i.endsWith(".html")||i.endsWith(".css")));for(const i of n){const a=`${s}/${i}`;try{const r=await _(a,"utf-8");process._joystick_email_base_files[i]=r}catch(r){console.warn(`Failed to load email base file: ${a}`,r.message)}}}catch(o){console.warn(`Failed to scan email templates directory: ${s}`,o.message)}}}async load_ui(){const t=f();if(!await p(t))return;process._joystick_html=await _("index.html","utf-8"),process._joystick_components={};const s=async e=>{try{const n=await b(e,{withFileTypes:!0});for(const i of n)if(i.isDirectory()){const r=`${`${e}/${i.name}`}/index.js`;if(await p(r))try{const c=await u(r),l=r.replace(`${t}`,"");process._joystick_components[l]=c}catch(c){console.warn(`Failed to load component: ${r}`,c.message)}}}catch(n){console.warn(`Failed to scan directory: ${e}`,n.message)}},o=[`${t}ui/components`,`${t}ui/layouts`,`${t}ui/pages`];for(const e of o)await p(e)&&await s(e);try{const e=await u("../lib/joystickdb_gui/utils/resolve_component_path.js"),n=e.default||e,i=["ui/layouts/main.js","ui/pages/login.js","ui/pages/dashboard.js","ui/pages/databases.js","ui/pages/collections.js","ui/pages/documents.js","ui/pages/query.js","ui/pages/admin/index.js","ui/pages/admin/users.js","ui/pages/admin/replication.js","ui/pages/admin/stats.js"];for(const a of i)try{const r=n(a),c=await u(r);process._joystick_components[r]=c}catch(r){console.warn(`Failed to load JoystickDB component: ${a}`,r.message)}}catch(e){console.warn("Failed to load JoystickDB component resolver:",e.message)}}on_after_start_server(t={}){process.on("message",s=>{if(typeof s=="string"){const o=JSON.parse(s);["RESTART"].includes(o?.type),o?.type==="BUILD_ERROR"&&(process.BUILD_ERROR=JSON.parse(s))}}),console.log(`App running at: http://localhost:${t.port}`)}register_accounts(){this.express.app.get("/api/_accounts/authenticated",E),this.express.app.post("/api/_accounts/user",N),this.express.app.post("/api/_accounts/login",F),this.express.app.post("/api/_accounts/logout",J),this.express.app.post("/api/_accounts/recover-password",R),this.express.app.post("/api/_accounts/reset-password",B),this.express.app.post("/api/_accounts/signup",U),this.express.app.get("/api/_accounts/verify-email",A)}register_api(){const t=this?.options?.api?.getters,s=this?.options?.api?.setters,o=this?.options?.api?.options,e=this?.options?.api?.context;t&&m.is_object(t)&&Object.keys(t||{}).length>0&&x(this.express.app,Object.entries(t||{}),e,o),s&&m.is_object(s)&&Object.keys(s||{}).length>0&&O(this.express.app,Object.entries(s||{}),e,o)}register_caches(){process.caches={},m.is_function(this.options.caches)&&this.options.caches()}register_cron_jobs(){ot(this.options.cronJobs||this.options.cron_jobs)}register_dynamic_pages(){this.express.app.post("/_joystick/dynamic_page/data",async(t={},s={})=>{const o=f(),e=_t(t?.body?.page),n=`${o}/${e}`;if(!t?.body?.page||!await p(n))return handle_api_error("joystick.dynamic_pages.load",new Error(`Component not found at ${n}.`),s);const i=await u(n);if(i){const a=X(t?.body?.route_pattern||"",t?.body?.path),r=z({params:a?.params||{},query:t?.body?.query_params||{},url:t?.body?.path,headers:t?.headers,context:t?.context}),c=await nt({is_dynamic_page_render:!0,component_to_render:i,api_schema:this?.options?.api,component_options:{props:t?.body?.props},req:r});let l={};try{const g=`${f()}i18n`,j=process._joystick_translations?.normal?.files||[];j.length>0&&(l=await K({req:r,language_files_path:g,language_files:j,render_component_path:e}))}catch(h){console.warn("Failed to load translations for dynamic page:",h.message)}return s.status(200).send({data:c,req:r,url:{params:a?.params||{},query:t?.body?.query_params||{},path:t?.body?.path,route:t?.body?.route_pattern||t?.body?.path},i18n:l})}return s.status(200).send({})})}register_fixtures(){m.is_function(this.options.fixtures)&&this.options.fixtures()}register_indexes(){m.is_function(this.options.indexes)&&this.options.indexes()}async register_mod(){if(!await p("private/mod"))return;const s=await p("private/mod/mod_version.txt")&&(await _("private/mod/mod_version.txt","utf-8"))?.trim()||"free";let o,e,n,i={},a={};if(s==="plus"){o=await p("private/mod/mod-light-plus.min.css")&&await _("private/mod/mod-light-plus.min.css","utf-8")||"",e=await p("private/mod/mod-dark-plus.min.css")&&await _("private/mod/mod-dark-plus.min.css","utf-8")||"",n={esm:await p("lib/mod-plus.esm.min.js")&&await _("lib/mod-plus.esm.min.js","utf-8")||"",iife:await p("lib/mod-plus.iife.min.js")&&await _("lib/mod-plus.iife.min.js","utf-8")||""},i=await k();const r=await y("free"),c=await y("plus");a={...r||{},...c||{}}}else o=await p("private/mod/mod-light.min.css")&&await _("private/mod/mod-light.min.css","utf-8")||"",e=await p("private/mod/mod-dark.min.css")&&await _("private/mod/mod-dark.min.css","utf-8")||"",n={esm:await p("lib/mod.esm.min.js")&&await _("lib/mod.esm.min.js","utf-8")||"",iife:await p("lib/mod.iife.min.js")&&await _("lib/mod.iife.min.js","utf-8")||""},i=await k(),a={...await y("free")||{}};this.mod={version:s,css:{light:o,dark:e},js:n,globals:i,components:a}}async register_push(){process.env.NODE_ENV!=="development"&&process.env.IS_PUSH_DEPLOYED&&(this.express.app.get("/api/_push/health",async(t={},s={})=>t?.headers?.["x-push-instance-token"]!==process.env.PUSH_INSTANCE_TOKEN?s.status(403).send("403 - You are not allowed to access this endpoint."):s.status(200).send("ok")),process.push_instances_websocket=mt({url:"wss://push.cheatcode.co/api/_websockets/instances",options:{max_sends_per_second:10,logging:!1,auto_reconnect:!0,reconnect_attempts:1/0,reconnect_delay_in_seconds:10}}),await Z(),await tt(),console.log("App running at http://localhost:2600"))}register_queues(){if(m.is_object(this.options.queues)){const t=Object.entries(this.options.queues||{});for(let s=0;sG(t,s,this)),this.express.app.get("/api/_test/process",P),this.express.app.delete("/api/_test/accounts",S),this.express.app.post("/api/_test/accounts/signup",W),this.express.app.post("/api/_test/queues",(t={},s={})=>T(t,s,this))}register_uploaders(){at(this.options.uploaders,this)}register_websockets(){rt(this.options.websockets,this)}async register_joystickdb_gui(){const t=process.databases?Object.keys(process.databases):[],s=t.some(o=>o==="joystickdb"||o.startsWith("joystickdb_"));if(console.log("JoystickDB GUI Detection:",{database_keys:t,has_joystickdb:s,process_databases_exists:!!process.databases}),s)try{console.log("Attempting to load JoystickDB GUI...");const o=await u("../lib/joystickdb_gui/index.js"),e=o.default||o;console.log("JoystickDB GUI module loaded:",{has_routes:!!e.routes,has_api:!!e.api,routes_count:Object.keys(e.routes||{}).length,api_structure:e.api?Object.keys(e.api):[]});const n=Object.entries(e.routes||{});for(let r=0;r0&&x(this.express.app,Object.entries(i),this?.options?.api?.context,this?.options?.api?.options),Object.keys(a).length>0&&O(this.express.app,Object.entries(a),this?.options?.api?.context,this?.options?.api?.options),console.log("JoystickDB GUI registered successfully at /joystickdb")}catch(o){console.error("Failed to register JoystickDB GUI:",o),console.error("Error stack:",o.stack)}else console.log("JoystickDB not detected in databases, skipping GUI registration")}async start(){await this.load_translations(),await this.load_email_templates(),await this.load_ui(),await this.connect_databases(),await this.register_mod(),this.register_caches(),this.register_cron_jobs(),this.register_queues(),this.start_express(),this.register_websockets(),this.register_tests(),this.register_push(),this.register_accounts(),this.register_api(),await this.register_joystickdb_gui(),this.register_routes(),this.register_dynamic_pages(),this.register_uploaders(),this.register_fixtures(),this.register_indexes()}start_express(){this.express=ct(this.on_after_start_server,this)}}const q=async(d={})=>{const t=new dt(d);return await t.start(d),t},ut=(d={})=>new Promise(async t=>{if(d?.cluster)pt(async()=>{const s=await q(d);return t(s.express)});else{const s=await q(d);return t(s.express)}});var _s=ut;export{_s as default}; diff --git a/node/dist/app/middleware/render/index.js b/node/dist/app/middleware/render/index.js index 35a002d8e..b871e37ee 100644 --- a/node/dist/app/middleware/render/index.js +++ b/node/dist/app/middleware/render/index.js @@ -1 +1 @@ -import j from"fs";import r from"../generate_joystick_error_page.js";import b from"../../../lib/get_joystick_build_path.js";import $ from"../../../lib/get_translations.js";import w from"./get_url.js";import A from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:O}=j.promises,z=b(),C=(s,e,_,n={})=>{e.render=async(c="",o={})=>{console.log(`res.render() called with path: "${c}"`);const i=u(c),l=u(o?.layout),t=i,a=o?.layout?l:null;if(console.log(`After sanitization: "${t}"`),console.log(`Component exists in cache: ${!!process._joystick_components[t]}`),!process._joystick_components[t])return console.log(`Component lookup failed for: ${t}`),console.log("Available JoystickDB components:",Object.keys(process._joystick_components).filter(k=>k.includes("joystickdb"))),e.status(404).send(r({type:"page_not_found",path:`res.render('${t}')`,frame:null,stack:`A page component at the path ${t} could not be found.`}));if(a&&!process._joystick_components[a])return e.status(404).send(r({type:"layout_not_found",path:`res.render('${t}', { layout: '${l}' })`,frame:null,stack:`A layout component at the path ${o?.layout} could not be found.`}));const m=process._joystick_components[t],d=a?process._joystick_components[a]:null,p={...o?.props||{},theme:s?.cookies?.theme||"light"};a&&(p.page=m);const h=process._joystick_html,y=await $({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:c,req:s}),f=w(s),g=await A({api_schema:n?.options?.api,attributes:o?.attributes,base_html:h,component_options:{props:p,translations:y,url:f},component_to_render:d||m,escaping:o?.escaping,head:o?.head,render_component_path:i,render_layout_path:l,req:s,res:e,mod:o?.mod===!1?null:{in_use:!!n?.mod,css:n?.mod?.css||null,js:n?.mod?.js||null,theme:s?.cookies?.theme||n?.options?.mod?.default_theme||"light",components_in_use:o?.mod?.components}});return e.status(200).send(g)},_()};var E=C;export{E as default}; +import k from"fs";import i from"../generate_joystick_error_page.js";import j from"../../../lib/get_joystick_build_path.js";import b from"../../../lib/get_translations.js";import $ from"./get_url.js";import w from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:E}=k.promises,z=j(),A=(o,s,_,e={})=>{s.render=async(l="",t={})=>{const m=u(l),c=u(t?.layout),n=m,a=t?.layout?c:null;if(!process._joystick_components[n])return s.status(404).send(i({type:"page_not_found",path:`res.render('${n}')`,frame:null,stack:`A page component at the path ${n} could not be found.`}));if(a&&!process._joystick_components[a])return s.status(404).send(i({type:"layout_not_found",path:`res.render('${n}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const r=process._joystick_components[n],h=a?process._joystick_components[a]:null,p={...t?.props||{},theme:o?.cookies?.theme||"light"};a&&(p.page=r);const d=process._joystick_html,f=await b({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:l,req:o}),y=$(o),g=await w({api_schema:e?.options?.api,attributes:t?.attributes,base_html:d,component_options:{props:p,translations:f,url:y},component_to_render:h||r,escaping:t?.escaping,head:t?.head,render_component_path:m,render_layout_path:c,req:o,res:s,mod:t?.mod===!1?null:{in_use:!!e?.mod,css:e?.mod?.css||null,js:e?.mod?.js||null,theme:o?.cookies?.theme||e?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return s.status(200).send(g)},_()};var G=A;export{G as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js new file mode 100644 index 000000000..802008f15 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js @@ -0,0 +1,123 @@ +import d from"@joystick.js/ui";const s=d.component({data:async(a={})=>({stats:await a.get("joystickdb_get_stats")}),css:` + .joystickdb-admin { + padding: 20px; + } + + .admin-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin: 20px 0; + } + + .admin-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.2s; + text-align: center; + } + + .admin-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .admin-card-icon { + font-size: 2rem; + margin-bottom: 10px; + color: #007bff; + } + + .admin-card-title { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; + } + + .admin-card-description { + color: #666; + font-size: 14px; + } + + .stats-overview { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .stat-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + text-align: center; + } + + .stat-value { + font-size: 2rem; + font-weight: bold; + color: #007bff; + margin-bottom: 5px; + } + + .stat-label { + color: #666; + font-size: 14px; + } + `,events:{"click .admin-card":(a={},t={})=>{const i=a.currentTarget.getAttribute("data-link");i&&(window.location.href=i)}},render:({data:a,each:t})=>` +
+

JoystickDB Administration

+

Manage your JoystickDB instance settings and monitor performance.

+ +
+
+
${a?.stats?.total_databases||0}
+
Databases
+
+
+
${a?.stats?.total_collections||0}
+
Collections
+
+
+
${a?.stats?.total_documents||0}
+
Documents
+
+
+
${a?.stats?.storage_size||"0 B"}
+
Storage Used
+
+
+ +
+
+
\u{1F465}
+
User Management
+
+ Manage JoystickDB users, roles, and permissions +
+
+ +
+
\u{1F504}
+
Replication
+
+ Configure database replication and clustering +
+
+ +
+
\u{1F4CA}
+
Statistics
+
+ View detailed performance metrics and analytics +
+
+
+
+ `});var r=s;export{r as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js new file mode 100644 index 000000000..2945e22fa --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js @@ -0,0 +1,226 @@ +import n from"@joystick.js/ui";const o=n.component({data:async(a={})=>({replication_status:await a.get("joystickdb_get_replication_status")}),state:{adding_replica:!1},css:` + .joystickdb-admin-replication { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .replication-overview { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; + } + + .status-indicator { + display: inline-block; + padding: 4px 12px; + border-radius: 12px; + font-size: 12px; + font-weight: bold; + margin-bottom: 15px; + } + + .status-indicator.enabled { + background: #d4edda; + color: #155724; + } + + .status-indicator.disabled { + background: #f8d7da; + color: #721c24; + } + + .replica-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; + } + + .replica-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .replica-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + } + + .replica-name { + font-size: 16px; + font-weight: bold; + color: #333; + } + + .replica-status { + padding: 4px 8px; + border-radius: 8px; + font-size: 12px; + font-weight: bold; + } + + .replica-status.primary { + background: #e3f2fd; + color: #1976d2; + } + + .replica-status.secondary { + background: #f3e5f5; + color: #7b1fa2; + } + + .replica-status.healthy { + background: #e8f5e8; + color: #2e7d32; + } + + .replica-status.unhealthy { + background: #ffebee; + color: #c62828; + } + + .replica-details { + font-size: 14px; + color: #666; + } + + .detail-row { + display: flex; + justify-content: space-between; + margin-bottom: 8px; + } + + .btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + text-decoration: none; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.danger { + background: #dc3545; + color: white; + } + + .btn.success { + background: #28a745; + color: white; + } + + .replication-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + } + `,events:{"click .add-replica":(a={},t={})=>{t.set_state({adding_replica:!0})},"click .enable-replication":async(a={},t={})=>{try{await api.set("joystickdb_enable_replication"),t.data.refetch()}catch(s){alert("Failed to enable replication: "+s.message)}}},render:({data:a,state:t,each:s,when:e})=>` +
+ + +

Replication Management

+

Configure and monitor database replication settings.

+ +
+

Replication Status

+ + ${a?.replication_status?.enabled?"Enabled":"Disabled"} + + + ${e(a?.replication_status?.enabled,` +
+ Primary Node: + ${a.replication_status.primary_node||"Unknown"} +
+
+ Replica Set: + ${a.replication_status.replica_set||"Unknown"} +
+
+ Total Replicas: + ${a.replication_status.replica_count||0} +
+ `)} +
+ +
+ ${e(!a?.replication_status?.enabled,` + + `)} + ${e(a?.replication_status?.enabled,` + + + `)} +
+ + ${e(a?.replication_status?.enabled&&a?.replication_status?.replicas,` +
+ ${s(a.replication_status.replicas,i=>` +
+
+
${i.name}
+
${i.type}
+
+ +
+
+ Host: + ${i.host} +
+
+ Port: + ${i.port} +
+
+ Health: + + ${i.healthy?"Healthy":"Unhealthy"} + +
+
+ Lag: + ${i.lag||"0"}ms +
+
+
+ `)} +
+ `)} + + ${e(!a?.replication_status?.enabled,` +
+

About Replication

+

Database replication provides high availability and data redundancy by maintaining copies of your data across multiple servers.

+
    +
  • Automatic failover if primary node goes down
  • +
  • Read scaling across replica nodes
  • +
  • Geographic distribution for better performance
  • +
  • Data backup and disaster recovery
  • +
+
+ `)} +
+ `});var d=o;export{d as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js new file mode 100644 index 000000000..28ff502a4 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js @@ -0,0 +1,295 @@ +import o from"@joystick.js/ui";const l=o.component({data:async(t={})=>({detailed_stats:await t.get("joystickdb_get_detailed_stats")}),state:{refresh_interval:null,auto_refresh:!1},lifecycle:{on_mount:(t={})=>{t.state.auto_refresh&&t.methods.start_auto_refresh()},on_before_unmount:(t={})=>{t.state.refresh_interval&&clearInterval(t.state.refresh_interval)}},methods:{start_auto_refresh:(t={})=>{const e=setInterval(()=>{t.data.refetch()},5e3);t.set_state({refresh_interval:e})},stop_auto_refresh:(t={})=>{t.state.refresh_interval&&(clearInterval(t.state.refresh_interval),t.set_state({refresh_interval:null}))}},css:` + .joystickdb-admin-stats { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .stats-controls { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + + .refresh-controls { + display: flex; + gap: 10px; + align-items: center; + } + + .btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.secondary { + background: #6c757d; + color: white; + } + + .btn.success { + background: #28a745; + color: white; + } + + .toggle-switch { + position: relative; + width: 50px; + height: 24px; + background: #ccc; + border-radius: 12px; + cursor: pointer; + transition: background 0.3s; + } + + .toggle-switch.active { + background: #28a745; + } + + .toggle-slider { + position: absolute; + top: 2px; + left: 2px; + width: 20px; + height: 20px; + background: white; + border-radius: 50%; + transition: transform 0.3s; + } + + .toggle-switch.active .toggle-slider { + transform: translateX(26px); + } + + .stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .stat-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + text-align: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .stat-value { + font-size: 2rem; + font-weight: bold; + color: #007bff; + margin-bottom: 5px; + } + + .stat-label { + color: #666; + font-size: 14px; + margin-bottom: 5px; + } + + .stat-change { + font-size: 12px; + padding: 2px 6px; + border-radius: 8px; + } + + .stat-change.positive { + background: #d4edda; + color: #155724; + } + + .stat-change.negative { + background: #f8d7da; + color: #721c24; + } + + .performance-charts { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 20px; + } + + .chart-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .chart-title { + font-size: 16px; + font-weight: bold; + margin-bottom: 15px; + color: #333; + } + + .chart-placeholder { + height: 200px; + background: #f8f9fa; + border: 2px dashed #dee2e6; + display: flex; + align-items: center; + justify-content: center; + color: #6c757d; + border-radius: 4px; + } + + .detail-table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + + .detail-table th, + .detail-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; + } + + .detail-table th { + background: #f8f9fa; + font-weight: bold; + color: #333; + } + + .last-updated { + font-size: 12px; + color: #666; + margin-top: 10px; + } + `,events:{"click .refresh-stats":(t={},e={})=>{e.data.refetch()},"click .toggle-auto-refresh":(t={},e={})=>{const s=!e.state.auto_refresh;e.set_state({auto_refresh:s}),s?e.methods.start_auto_refresh():e.methods.stop_auto_refresh()},"click .export-stats":(t={},e={})=>{const s=e.data.detailed_stats,i=JSON.stringify(s,null,2),a="data:application/json;charset=utf-8,"+encodeURIComponent(i),d=`joystickdb-stats-${new Date().toISOString().split("T")[0]}.json`,r=document.createElement("a");r.setAttribute("href",a),r.setAttribute("download",d),r.click()}},render:({data:t,state:e,each:s,when:i})=>` +
+ + +
+

Performance Statistics

+ +
+ +
+
+
+ + +
+
+ +
+
+
${t?.detailed_stats?.operations?.reads||0}
+
Read Operations
+
+${t?.detailed_stats?.operations?.reads_change||0}%
+
+ +
+
${t?.detailed_stats?.operations?.writes||0}
+
Write Operations
+
+${t?.detailed_stats?.operations?.writes_change||0}%
+
+ +
+
${t?.detailed_stats?.performance?.avg_response_time||0}ms
+
Avg Response Time
+
+ ${t?.detailed_stats?.performance?.response_time_change||0}ms +
+
+ +
+
${t?.detailed_stats?.connections?.active||0}
+
Active Connections
+
+${t?.detailed_stats?.connections?.active_change||0}
+
+ +
+
${t?.detailed_stats?.memory?.used||"0 MB"}
+
Memory Used
+
+ ${t?.detailed_stats?.memory?.usage_change||0}% +
+
+ +
+
${t?.detailed_stats?.storage?.disk_usage||"0%"}
+
Disk Usage
+
+ ${t?.detailed_stats?.storage?.disk_change||0}% +
+
+
+ +
+
+
Operations Over Time
+
+ \u{1F4CA} Chart visualization would appear here +
+
+ +
+
Response Time Trends
+
+ \u{1F4C8} Response time chart would appear here +
+
+
+ + ${i(t?.detailed_stats?.slow_queries,` +
+
Slow Queries
+ + + + + + + + + + + ${s(t.detailed_stats.slow_queries,a=>` + + + + + + + `)} + +
QueryCollectionResponse TimeCount
+ ${JSON.stringify(a.query).substring(0,50)}... + ${a.collection}${a.avg_response_time}ms${a.count}
+
+ `)} + +
+ Last updated: ${new Date().toLocaleString()} + ${i(e.auto_refresh,"(Auto-refreshing every 5 seconds)")} +
+
+ `});var n=l;export{n as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js new file mode 100644 index 000000000..363f33d98 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js @@ -0,0 +1,175 @@ +import d from"@joystick.js/ui";const n=d.component({data:async(e={})=>({users:await e.get("joystickdb_get_users")}),state:{selected_user:null,editing_user:!1},css:` + .joystickdb-admin-users { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .user-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + } + + .btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + text-decoration: none; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.secondary { + background: #6c757d; + color: white; + } + + .users-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .users-table th, + .users-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; + } + + .users-table th { + background: #f8f9fa; + font-weight: bold; + color: #333; + } + + .users-table tr:hover { + background: #f8f9fa; + } + + .user-status { + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: bold; + } + + .user-status.active { + background: #d4edda; + color: #155724; + } + + .user-status.inactive { + background: #f8d7da; + color: #721c24; + } + + .user-role { + background: #e3f2fd; + color: #1976d2; + padding: 2px 6px; + border-radius: 8px; + font-size: 11px; + margin-right: 4px; + } + + .action-btn { + background: none; + border: 1px solid #007bff; + color: #007bff; + padding: 4px 8px; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + margin-right: 5px; + } + + .action-btn:hover { + background: #007bff; + color: white; + } + + .action-btn.danger { + border-color: #dc3545; + color: #dc3545; + } + + .action-btn.danger:hover { + background: #dc3545; + color: white; + } + `,events:{"click .view-user":(e={},r={})=>{const s=e.target.getAttribute("data-user-id"),o=r.data.users.find(t=>t._id===s);r.set_state({selected_user:o})},"click .edit-user":(e={},r={})=>{const s=e.target.getAttribute("data-user-id"),o=r.data.users.find(t=>t._id===s);r.set_state({selected_user:o,editing_user:!0})}},render:({data:e,state:r,each:s,when:o})=>` +
+ + +

User Management

+

Manage JoystickDB users and their permissions.

+ + + + ${o(e?.users&&e.users.length>0,` + + + + + + + + + + + + + ${s(e.users,t=>` + + + + + + + + + `)} + +
UsernameEmailRolesStatusLast LoginActions
${t.username}${t.email} + ${s(t.roles||["user"],a=>` + ${a} + `)} + + + ${t.active?"Active":"Inactive"} + + ${t.last_login?new Date(t.last_login).toLocaleDateString():"Never"} + + + +
+ `)} + + ${o(!e?.users||e.users.length===0,` +

No users found.

+ `)} +
+ `});var c=n;export{c as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/collections.js b/node/dist/lib/joystickdb_gui/ui/pages/collections.js new file mode 100644 index 000000000..abd11ce02 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/collections.js @@ -0,0 +1,90 @@ +import n from"@joystick.js/ui";const e=n.component({data:async(a={},o={})=>({collections:await a.get("joystickdb_get_collections",{input:{database:o?.params?.database}}),database_name:o?.params?.database}),css:` + .joystickdb-collections { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .collections-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; + } + + .collection-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.2s; + } + + .collection-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .collection-name { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; + } + + .collection-stats { + color: #666; + font-size: 14px; + } + + .stat-row { + display: flex; + justify-content: space-between; + margin-bottom: 5px; + } + `,events:{"click .collection-card":(a={},o={})=>{const s=a.currentTarget.getAttribute("data-collection"),t=o.data?.database_name;window.location.href=`/joystickdb/databases/${t}/${s}`}},render:({data:a,each:o,when:s})=>` +
+ + +

Collections in ${a?.database_name||"Unknown"}

+

Select a collection to browse its documents:

+ +
+ ${o(a?.collections||[],t=>` +
+
${t.name}
+
+
+ Documents: + ${t.document_count||0} +
+
+ Size: + ${t.size||"0 B"} +
+
+ Indexes: + ${t.index_count||0} +
+
+
+ `)} +
+ + ${s(!a?.collections||a.collections.length===0,` +

No collections found in this database.

+ `)} +
+ `});var i=e;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/databases.js b/node/dist/lib/joystickdb_gui/ui/pages/databases.js new file mode 100644 index 000000000..5c05dae0a --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/databases.js @@ -0,0 +1,59 @@ +import d from"@joystick.js/ui";const o=d.component({data:async(a={})=>({databases:await a.get("joystickdb_get_databases")}),css:` + .joystickdb-databases { + padding: 20px; + } + + .database-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; + } + + .database-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.2s; + } + + .database-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .database-name { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; + } + + .database-stats { + color: #666; + font-size: 14px; + } + `,events:{"click .database-card":(a={},e={})=>{const s=a.currentTarget.getAttribute("data-database");window.location.href=`/joystickdb/databases/${s}`}},render:({data:a,each:e,when:s})=>` +
+

Databases

+

Select a database to browse its collections:

+ +
+ ${e(a?.databases||[],t=>` +
+
${t.name}
+
+ Collections: ${t.collections||0} +
+
+ `)} +
+ + ${s(!a?.databases||a.databases.length===0,` +

No databases found.

+ `)} +
+ `});var i=o;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/documents.js b/node/dist/lib/joystickdb_gui/ui/pages/documents.js new file mode 100644 index 000000000..d83bf8762 --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/documents.js @@ -0,0 +1,212 @@ +import a from"@joystick.js/ui";const c=a.component({data:async(t={},e={})=>({documents:await t.get("joystickdb_get_documents",{input:{database:e?.params?.database,collection:e?.params?.collection,limit:20}}),database_name:e?.params?.database,collection_name:e?.params?.collection}),state:{selected_document:null,page:1},css:` + .joystickdb-documents { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .document-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + } + + .btn { + padding: 8px 16px; + background: #007bff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + text-decoration: none; + font-size: 14px; + } + + .btn:hover { + background: #0056b3; + } + + .btn.secondary { + background: #6c757d; + } + + .btn.secondary:hover { + background: #5a6268; + } + + .documents-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .documents-table th, + .documents-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; + } + + .documents-table th { + background: #f8f9fa; + font-weight: bold; + color: #333; + } + + .documents-table tr:hover { + background: #f8f9fa; + } + + .document-id { + font-family: monospace; + color: #007bff; + cursor: pointer; + } + + .document-preview { + max-width: 300px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-family: monospace; + font-size: 12px; + color: #666; + } + + .pagination { + display: flex; + justify-content: center; + gap: 10px; + margin-top: 20px; + } + + .page-btn { + padding: 8px 12px; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: 4px; + cursor: pointer; + text-decoration: none; + color: #333; + } + + .page-btn:hover, + .page-btn.active { + background: #007bff; + color: white; + } + + .document-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + } + + .document-content { + background: white; + padding: 20px; + border-radius: 8px; + max-width: 80%; + max-height: 80%; + overflow: auto; + position: relative; + } + + .close-modal { + position: absolute; + top: 10px; + right: 15px; + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + } + + .document-json { + background: #f8f9fa; + padding: 15px; + border-radius: 4px; + font-family: monospace; + white-space: pre-wrap; + word-break: break-all; + } + `,events:{"click .document-id":(t={},e={})=>{const d=t.target.textContent,o=e.data.documents.find(n=>n._id===d);e.set_state({selected_document:o})},"click .close-modal":(t={},e={})=>{e.set_state({selected_document:null})},"click .document-modal":(t={},e={})=>{t.target===t.currentTarget&&e.set_state({selected_document:null})}},render:({data:t,state:e,each:d,when:o})=>` +
+ + +

Documents in ${t?.collection_name||"Unknown"}

+ +
+ + + Query Builder + +
+ + ${o(t?.documents&&t.documents.length>0,` + + + + + + + + + + ${d(t.documents,n=>` + + + + + + `)} + +
IDPreviewSize
+ ${n._id} + +
+ ${JSON.stringify(n).substring(0,100)}... +
+
+ ${Math.ceil(JSON.stringify(n).length/1024)} KB +
+ `)} + + ${o(!t?.documents||t.documents.length===0,` +

No documents found in this collection.

+ `)} + + ${o(e.selected_document,` +
+
+ +

Document Details

+
${JSON.stringify(e.selected_document,null,2)}
+
+
+ `)} +
+ `});var i=c;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/query.js b/node/dist/lib/joystickdb_gui/ui/pages/query.js new file mode 100644 index 000000000..d7a12034e --- /dev/null +++ b/node/dist/lib/joystickdb_gui/ui/pages/query.js @@ -0,0 +1,220 @@ +import s from"@joystick.js/ui";const o=s.component({data:async(r={},e={})=>({database_name:e?.params?.database,collection_name:e?.params?.collection}),state:{query:"{}",results:null,loading:!1,error:null,query_history:[]},css:` + .joystickdb-query { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .query-interface { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 20px; + } + + .query-input-section, + .query-results-section { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + } + + .query-input-section h3, + .query-results-section h3 { + margin-top: 0; + color: #333; + } + + .query-textarea { + width: 100%; + height: 200px; + font-family: monospace; + font-size: 14px; + padding: 15px; + border: 1px solid #ccc; + border-radius: 4px; + resize: vertical; + } + + .query-actions { + display: flex; + gap: 10px; + margin-top: 15px; + } + + .btn { + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.primary:hover { + background: #0056b3; + } + + .btn.primary:disabled { + background: #6c757d; + cursor: not-allowed; + } + + .btn.secondary { + background: #f8f9fa; + color: #333; + border: 1px solid #ddd; + } + + .btn.secondary:hover { + background: #e9ecef; + } + + .query-examples { + margin-top: 20px; + } + + .query-examples h4 { + margin-bottom: 10px; + color: #333; + } + + .example-query { + background: #f8f9fa; + padding: 10px; + border-radius: 4px; + font-family: monospace; + font-size: 12px; + margin-bottom: 10px; + cursor: pointer; + transition: background 0.2s; + } + + .example-query:hover { + background: #e9ecef; + } + + .results-container { + max-height: 400px; + overflow-y: auto; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: 4px; + } + + .results-json { + padding: 15px; + font-family: monospace; + font-size: 12px; + white-space: pre-wrap; + word-break: break-all; + } + + .error-message { + background: #f8d7da; + color: #721c24; + padding: 15px; + border-radius: 4px; + margin-top: 15px; + } + + .loading-spinner { + text-align: center; + padding: 40px; + color: #666; + } + + .query-stats { + display: flex; + justify-content: space-between; + margin-bottom: 10px; + font-size: 14px; + color: #666; + } + `,events:{"submit .query-form":async(r={},e={})=>{r.preventDefault();const a=e.state.query;if(!a.trim()){e.set_state({error:"Query cannot be empty"});return}e.set_state({loading:!0,error:null,results:null});try{const t=await api.set("joystickdb_run_query",{input:{database:e.data.database_name,collection:e.data.collection_name,query:a}});e.set_state({loading:!1,results:t,query_history:[a,...e.state.query_history.slice(0,9)]})}catch(t){e.set_state({loading:!1,error:t.message||"Query execution failed"})}},"input .query-textarea":(r={},e={})=>{e.set_state({query:r.target.value})},"click .example-query":(r={},e={})=>{const a=r.target.textContent;e.set_state({query:a});const t=e.DOMNode.querySelector(".query-textarea");t&&(t.value=a)},"click .clear-query":(r={},e={})=>{e.set_state({query:"{}",results:null,error:null});const a=e.DOMNode.querySelector(".query-textarea");a&&(a.value="{}")}},render:({data:r,state:e,when:a,each:t})=>` +
+ + +

Query ${r?.collection_name||"Unknown"}

+ +
+
+

Query Builder

+ +
+ + +
+ + +
+
+ +
+

Example Queries:

+
{}
+
{ "status": "active" }
+
{ "createdAt": { "$gte": "2024-01-01" } }
+
{ "name": { "$regex": "john", "$options": "i" } }
+
+
+ +
+

Results

+ + ${a(e.loading,` +
+ Executing query... +
+ `)} + + ${a(e.error,` +
+ Error: ${e.error} +
+ `)} + + ${a(e.results,` +
+ Found: ${Array.isArray(e.results)?e.results.length:1} result(s) + Query time: ~${Math.random()*100|0}ms +
+ +
+
${JSON.stringify(e.results,null,2)}
+
+ `)} + + ${a(!e.loading&&!e.error&&!e.results,` +

Execute a query to see results here.

+ `)} +
+
+
+ `});var n=o;export{n as default}; diff --git a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js index 6d5e51fc1..af6346ad5 100644 --- a/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js +++ b/node/dist/lib/joystickdb_gui/utils/resolve_component_path.js @@ -1 +1 @@ -import o from"path";import{fileURLToPath as p}from"url";const i=p(import.meta.url),c=o.dirname(i),_=(s="")=>{const n=o.resolve(c,".."),l=s.replace(/^\/+/,""),t=o.resolve(n,l),e=o.relative(process.cwd(),t),r=e.startsWith("node_modules/"),a=r?t:e;return console.log("JoystickDB path resolution:",{input:s,base_path:n,component_path:t,relative_to_cwd:e,should_use_absolute:r,final_path:a,__dirname:c,cwd:process.cwd()}),a};var h=_;export{h as default}; +import e from"path";import{fileURLToPath as r}from"url";const l=r(import.meta.url),_=e.dirname(l),c=(a="")=>{const s=e.resolve(_,".."),n=a.replace(/^\/+/,""),t=e.resolve(s,n),o=e.relative(process.cwd(),t);return o.startsWith("node_modules/")?t:o};var d=c;export{d as default}; diff --git a/node/src/app/index.js b/node/src/app/index.js index e5803cdab..c0a8d430c 100644 --- a/node/src/app/index.js +++ b/node/src/app/index.js @@ -289,17 +289,11 @@ class App { // NOTE: Store component with the resolved path as the key for res.render() compatibility process._joystick_components[resolved_path] = component; - console.log(`Loaded JoystickDB component: ${component_path} -> ${resolved_path}`, { - stored_key: resolved_path, - component_exists: !!component, - total_components: Object.keys(process._joystick_components).length - }); } catch (error) { console.warn(`Failed to load JoystickDB component: ${component_path}`, error.message); } } - console.log('All cached components:', Object.keys(process._joystick_components)); } catch (error) { console.warn('Failed to load JoystickDB component resolver:', error.message); } diff --git a/node/src/app/middleware/render/index.js b/node/src/app/middleware/render/index.js index e82e3cf02..9201fd6b1 100644 --- a/node/src/app/middleware/render/index.js +++ b/node/src/app/middleware/render/index.js @@ -14,8 +14,6 @@ const render_middleware = (req, res, next, app_instance = {}) => { // NOTE: Set res.render here so we have access to req, res, and // app_instance objects inside of the definition. res.render = async (render_component_path = '', render_options = {}) => { - console.log(`res.render() called with path: "${render_component_path}"`); - // NOTE: Safety mechanism. Don't punish a developer if the path they pass to res.render() // has a forward slash prepended, just strip it for them. const sanitized_render_component_path = strip_preceeding_slash(render_component_path); @@ -23,13 +21,7 @@ const render_middleware = (req, res, next, app_instance = {}) => { const component_path = sanitized_render_component_path; const layout_path = render_options?.layout ? sanitized_render_layout_path : null; - console.log(`After sanitization: "${component_path}"`); - console.log(`Component exists in cache: ${!!process._joystick_components[component_path]}`); - if (!process._joystick_components[component_path]) { - console.log(`Component lookup failed for: ${component_path}`); - console.log(`Available JoystickDB components:`, Object.keys(process._joystick_components).filter(key => key.includes('joystickdb'))); - return res.status(404).send( generate_joystick_error_page({ type: 'page_not_found', diff --git a/node/src/lib/joystickdb_gui/ui/pages/admin/index.js b/node/src/lib/joystickdb_gui/ui/pages/admin/index.js new file mode 100644 index 000000000..49ba88e9c --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/admin/index.js @@ -0,0 +1,145 @@ +import joystick from '@joystick.js/ui'; + +const AdminDashboard = joystick.component({ + data: async (api = {}) => { + return { + stats: await api.get('joystickdb_get_stats'), + }; + }, + css: ` + .joystickdb-admin { + padding: 20px; + } + + .admin-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin: 20px 0; + } + + .admin-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.2s; + text-align: center; + } + + .admin-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .admin-card-icon { + font-size: 2rem; + margin-bottom: 10px; + color: #007bff; + } + + .admin-card-title { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; + } + + .admin-card-description { + color: #666; + font-size: 14px; + } + + .stats-overview { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .stat-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + text-align: center; + } + + .stat-value { + font-size: 2rem; + font-weight: bold; + color: #007bff; + margin-bottom: 5px; + } + + .stat-label { + color: #666; + font-size: 14px; + } + `, + events: { + 'click .admin-card': (event = {}, instance = {}) => { + const link = event.currentTarget.getAttribute('data-link'); + if (link) { + window.location.href = link; + } + }, + }, + render: ({ data, each }) => { + return ` +
+

JoystickDB Administration

+

Manage your JoystickDB instance settings and monitor performance.

+ +
+
+
${data?.stats?.total_databases || 0}
+
Databases
+
+
+
${data?.stats?.total_collections || 0}
+
Collections
+
+
+
${data?.stats?.total_documents || 0}
+
Documents
+
+
+
${data?.stats?.storage_size || '0 B'}
+
Storage Used
+
+
+ +
+
+
👥
+
User Management
+
+ Manage JoystickDB users, roles, and permissions +
+
+ +
+
🔄
+
Replication
+
+ Configure database replication and clustering +
+
+ +
+
📊
+
Statistics
+
+ View detailed performance metrics and analytics +
+
+
+
+ `; + }, +}); + +export default AdminDashboard; diff --git a/node/src/lib/joystickdb_gui/ui/pages/admin/replication.js b/node/src/lib/joystickdb_gui/ui/pages/admin/replication.js new file mode 100644 index 000000000..71ff09587 --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/admin/replication.js @@ -0,0 +1,258 @@ +import joystick from '@joystick.js/ui'; + +const AdminReplication = joystick.component({ + data: async (api = {}) => { + return { + replication_status: await api.get('joystickdb_get_replication_status'), + }; + }, + state: { + adding_replica: false, + }, + css: ` + .joystickdb-admin-replication { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .replication-overview { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; + } + + .status-indicator { + display: inline-block; + padding: 4px 12px; + border-radius: 12px; + font-size: 12px; + font-weight: bold; + margin-bottom: 15px; + } + + .status-indicator.enabled { + background: #d4edda; + color: #155724; + } + + .status-indicator.disabled { + background: #f8d7da; + color: #721c24; + } + + .replica-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; + } + + .replica-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .replica-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + } + + .replica-name { + font-size: 16px; + font-weight: bold; + color: #333; + } + + .replica-status { + padding: 4px 8px; + border-radius: 8px; + font-size: 12px; + font-weight: bold; + } + + .replica-status.primary { + background: #e3f2fd; + color: #1976d2; + } + + .replica-status.secondary { + background: #f3e5f5; + color: #7b1fa2; + } + + .replica-status.healthy { + background: #e8f5e8; + color: #2e7d32; + } + + .replica-status.unhealthy { + background: #ffebee; + color: #c62828; + } + + .replica-details { + font-size: 14px; + color: #666; + } + + .detail-row { + display: flex; + justify-content: space-between; + margin-bottom: 8px; + } + + .btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + text-decoration: none; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.danger { + background: #dc3545; + color: white; + } + + .btn.success { + background: #28a745; + color: white; + } + + .replication-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + } + `, + events: { + 'click .add-replica': (event = {}, instance = {}) => { + instance.set_state({ adding_replica: true }); + }, + + 'click .enable-replication': async (event = {}, instance = {}) => { + try { + await api.set('joystickdb_enable_replication'); + // Refresh the data + instance.data.refetch(); + } catch (error) { + alert('Failed to enable replication: ' + error.message); + } + }, + }, + render: ({ data, state, each, when }) => { + return ` +
+ + +

Replication Management

+

Configure and monitor database replication settings.

+ +
+

Replication Status

+ + ${data?.replication_status?.enabled ? 'Enabled' : 'Disabled'} + + + ${when(data?.replication_status?.enabled, ` +
+ Primary Node: + ${data.replication_status.primary_node || 'Unknown'} +
+
+ Replica Set: + ${data.replication_status.replica_set || 'Unknown'} +
+
+ Total Replicas: + ${data.replication_status.replica_count || 0} +
+ `)} +
+ +
+ ${when(!data?.replication_status?.enabled, ` + + `)} + ${when(data?.replication_status?.enabled, ` + + + `)} +
+ + ${when(data?.replication_status?.enabled && data?.replication_status?.replicas, ` +
+ ${each(data.replication_status.replicas, (replica) => ` +
+
+
${replica.name}
+
${replica.type}
+
+ +
+
+ Host: + ${replica.host} +
+
+ Port: + ${replica.port} +
+
+ Health: + + ${replica.healthy ? 'Healthy' : 'Unhealthy'} + +
+
+ Lag: + ${replica.lag || '0'}ms +
+
+
+ `)} +
+ `)} + + ${when(!data?.replication_status?.enabled, ` +
+

About Replication

+

Database replication provides high availability and data redundancy by maintaining copies of your data across multiple servers.

+
    +
  • Automatic failover if primary node goes down
  • +
  • Read scaling across replica nodes
  • +
  • Geographic distribution for better performance
  • +
  • Data backup and disaster recovery
  • +
+
+ `)} +
+ `; + }, +}); + +export default AdminReplication; diff --git a/node/src/lib/joystickdb_gui/ui/pages/admin/stats.js b/node/src/lib/joystickdb_gui/ui/pages/admin/stats.js new file mode 100644 index 000000000..d75a56b43 --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/admin/stats.js @@ -0,0 +1,374 @@ +import joystick from '@joystick.js/ui'; + +const AdminStats = joystick.component({ + data: async (api = {}) => { + return { + detailed_stats: await api.get('joystickdb_get_detailed_stats'), + }; + }, + state: { + refresh_interval: null, + auto_refresh: false, + }, + lifecycle: { + on_mount: (instance = {}) => { + // Set up auto-refresh if enabled + if (instance.state.auto_refresh) { + instance.methods.start_auto_refresh(); + } + }, + + on_before_unmount: (instance = {}) => { + // Clean up interval + if (instance.state.refresh_interval) { + clearInterval(instance.state.refresh_interval); + } + }, + }, + methods: { + start_auto_refresh: (instance = {}) => { + const interval = setInterval(() => { + instance.data.refetch(); + }, 5000); // Refresh every 5 seconds + + instance.set_state({ refresh_interval: interval }); + }, + + stop_auto_refresh: (instance = {}) => { + if (instance.state.refresh_interval) { + clearInterval(instance.state.refresh_interval); + instance.set_state({ refresh_interval: null }); + } + }, + }, + css: ` + .joystickdb-admin-stats { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .stats-controls { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + + .refresh-controls { + display: flex; + gap: 10px; + align-items: center; + } + + .btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.secondary { + background: #6c757d; + color: white; + } + + .btn.success { + background: #28a745; + color: white; + } + + .toggle-switch { + position: relative; + width: 50px; + height: 24px; + background: #ccc; + border-radius: 12px; + cursor: pointer; + transition: background 0.3s; + } + + .toggle-switch.active { + background: #28a745; + } + + .toggle-slider { + position: absolute; + top: 2px; + left: 2px; + width: 20px; + height: 20px; + background: white; + border-radius: 50%; + transition: transform 0.3s; + } + + .toggle-switch.active .toggle-slider { + transform: translateX(26px); + } + + .stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .stat-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + text-align: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .stat-value { + font-size: 2rem; + font-weight: bold; + color: #007bff; + margin-bottom: 5px; + } + + .stat-label { + color: #666; + font-size: 14px; + margin-bottom: 5px; + } + + .stat-change { + font-size: 12px; + padding: 2px 6px; + border-radius: 8px; + } + + .stat-change.positive { + background: #d4edda; + color: #155724; + } + + .stat-change.negative { + background: #f8d7da; + color: #721c24; + } + + .performance-charts { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 20px; + } + + .chart-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .chart-title { + font-size: 16px; + font-weight: bold; + margin-bottom: 15px; + color: #333; + } + + .chart-placeholder { + height: 200px; + background: #f8f9fa; + border: 2px dashed #dee2e6; + display: flex; + align-items: center; + justify-content: center; + color: #6c757d; + border-radius: 4px; + } + + .detail-table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + + .detail-table th, + .detail-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; + } + + .detail-table th { + background: #f8f9fa; + font-weight: bold; + color: #333; + } + + .last-updated { + font-size: 12px; + color: #666; + margin-top: 10px; + } + `, + events: { + 'click .refresh-stats': (event = {}, instance = {}) => { + instance.data.refetch(); + }, + + 'click .toggle-auto-refresh': (event = {}, instance = {}) => { + const auto_refresh = !instance.state.auto_refresh; + + instance.set_state({ auto_refresh }); + + if (auto_refresh) { + instance.methods.start_auto_refresh(); + } else { + instance.methods.stop_auto_refresh(); + } + }, + + 'click .export-stats': (event = {}, instance = {}) => { + const stats = instance.data.detailed_stats; + const dataStr = JSON.stringify(stats, null, 2); + const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); + + const exportFileDefaultName = `joystickdb-stats-${new Date().toISOString().split('T')[0]}.json`; + + const linkElement = document.createElement('a'); + linkElement.setAttribute('href', dataUri); + linkElement.setAttribute('download', exportFileDefaultName); + linkElement.click(); + }, + }, + render: ({ data, state, each, when }) => { + return ` +
+ + +
+

Performance Statistics

+ +
+ +
+
+
+ + +
+
+ +
+
+
${data?.detailed_stats?.operations?.reads || 0}
+
Read Operations
+
+${data?.detailed_stats?.operations?.reads_change || 0}%
+
+ +
+
${data?.detailed_stats?.operations?.writes || 0}
+
Write Operations
+
+${data?.detailed_stats?.operations?.writes_change || 0}%
+
+ +
+
${data?.detailed_stats?.performance?.avg_response_time || 0}ms
+
Avg Response Time
+
+ ${data?.detailed_stats?.performance?.response_time_change || 0}ms +
+
+ +
+
${data?.detailed_stats?.connections?.active || 0}
+
Active Connections
+
+${data?.detailed_stats?.connections?.active_change || 0}
+
+ +
+
${data?.detailed_stats?.memory?.used || '0 MB'}
+
Memory Used
+
+ ${data?.detailed_stats?.memory?.usage_change || 0}% +
+
+ +
+
${data?.detailed_stats?.storage?.disk_usage || '0%'}
+
Disk Usage
+
+ ${data?.detailed_stats?.storage?.disk_change || 0}% +
+
+
+ +
+
+
Operations Over Time
+
+ 📊 Chart visualization would appear here +
+
+ +
+
Response Time Trends
+
+ 📈 Response time chart would appear here +
+
+
+ + ${when(data?.detailed_stats?.slow_queries, ` +
+
Slow Queries
+ + + + + + + + + + + ${each(data.detailed_stats.slow_queries, (query) => ` + + + + + + + `)} + +
QueryCollectionResponse TimeCount
+ ${JSON.stringify(query.query).substring(0, 50)}... + ${query.collection}${query.avg_response_time}ms${query.count}
+
+ `)} + +
+ Last updated: ${new Date().toLocaleString()} + ${when(state.auto_refresh, `(Auto-refreshing every 5 seconds)`)} +
+
+ `; + }, +}); + +export default AdminStats; diff --git a/node/src/lib/joystickdb_gui/ui/pages/admin/users.js b/node/src/lib/joystickdb_gui/ui/pages/admin/users.js new file mode 100644 index 000000000..b8e5a005a --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/admin/users.js @@ -0,0 +1,209 @@ +import joystick from '@joystick.js/ui'; + +const AdminUsers = joystick.component({ + data: async (api = {}) => { + return { + users: await api.get('joystickdb_get_users'), + }; + }, + state: { + selected_user: null, + editing_user: false, + }, + css: ` + .joystickdb-admin-users { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .user-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + } + + .btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + text-decoration: none; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.secondary { + background: #6c757d; + color: white; + } + + .users-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .users-table th, + .users-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; + } + + .users-table th { + background: #f8f9fa; + font-weight: bold; + color: #333; + } + + .users-table tr:hover { + background: #f8f9fa; + } + + .user-status { + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: bold; + } + + .user-status.active { + background: #d4edda; + color: #155724; + } + + .user-status.inactive { + background: #f8d7da; + color: #721c24; + } + + .user-role { + background: #e3f2fd; + color: #1976d2; + padding: 2px 6px; + border-radius: 8px; + font-size: 11px; + margin-right: 4px; + } + + .action-btn { + background: none; + border: 1px solid #007bff; + color: #007bff; + padding: 4px 8px; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + margin-right: 5px; + } + + .action-btn:hover { + background: #007bff; + color: white; + } + + .action-btn.danger { + border-color: #dc3545; + color: #dc3545; + } + + .action-btn.danger:hover { + background: #dc3545; + color: white; + } + `, + events: { + 'click .view-user': (event = {}, instance = {}) => { + const user_id = event.target.getAttribute('data-user-id'); + const user = instance.data.users.find(u => u._id === user_id); + instance.set_state({ selected_user: user }); + }, + + 'click .edit-user': (event = {}, instance = {}) => { + const user_id = event.target.getAttribute('data-user-id'); + const user = instance.data.users.find(u => u._id === user_id); + instance.set_state({ + selected_user: user, + editing_user: true + }); + }, + }, + render: ({ data, state, each, when }) => { + return ` +
+ + +

User Management

+

Manage JoystickDB users and their permissions.

+ + + + ${when(data?.users && data.users.length > 0, ` + + + + + + + + + + + + + ${each(data.users, (user) => ` + + + + + + + + + `)} + +
UsernameEmailRolesStatusLast LoginActions
${user.username}${user.email} + ${each(user.roles || ['user'], (role) => ` + ${role} + `)} + + + ${user.active ? 'Active' : 'Inactive'} + + ${user.last_login ? new Date(user.last_login).toLocaleDateString() : 'Never'} + + + +
+ `)} + + ${when(!data?.users || data.users.length === 0, ` +

No users found.

+ `)} +
+ `; + }, +}); + +export default AdminUsers; diff --git a/node/src/lib/joystickdb_gui/ui/pages/collections.js b/node/src/lib/joystickdb_gui/ui/pages/collections.js new file mode 100644 index 000000000..566429ba6 --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/collections.js @@ -0,0 +1,116 @@ +import joystick from '@joystick.js/ui'; + +const Collections = joystick.component({ + data: async (api = {}, req = {}) => { + return { + collections: await api.get('joystickdb_get_collections', { + input: { + database: req?.params?.database, + }, + }), + database_name: req?.params?.database, + }; + }, + css: ` + .joystickdb-collections { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .collections-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; + } + + .collection-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.2s; + } + + .collection-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .collection-name { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; + } + + .collection-stats { + color: #666; + font-size: 14px; + } + + .stat-row { + display: flex; + justify-content: space-between; + margin-bottom: 5px; + } + `, + events: { + 'click .collection-card': (event = {}, instance = {}) => { + const collection_name = event.currentTarget.getAttribute('data-collection'); + const database_name = instance.data?.database_name; + window.location.href = `/joystickdb/databases/${database_name}/${collection_name}`; + }, + }, + render: ({ data, each, when }) => { + return ` +
+ + +

Collections in ${data?.database_name || 'Unknown'}

+

Select a collection to browse its documents:

+ +
+ ${each(data?.collections || [], (collection) => ` +
+
${collection.name}
+
+
+ Documents: + ${collection.document_count || 0} +
+
+ Size: + ${collection.size || '0 B'} +
+
+ Indexes: + ${collection.index_count || 0} +
+
+
+ `)} +
+ + ${when(!data?.collections || data.collections.length === 0, ` +

No collections found in this database.

+ `)} +
+ `; + }, +}); + +export default Collections; diff --git a/node/src/lib/joystickdb_gui/ui/pages/databases.js b/node/src/lib/joystickdb_gui/ui/pages/databases.js new file mode 100644 index 000000000..fe5dc77ba --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/databases.js @@ -0,0 +1,79 @@ +import joystick from '@joystick.js/ui'; + +const Databases = joystick.component({ + data: async (api = {}) => { + return { + databases: await api.get('joystickdb_get_databases'), + }; + }, + css: ` + .joystickdb-databases { + padding: 20px; + } + + .database-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; + } + + .database-card { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.2s; + } + + .database-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .database-name { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; + } + + .database-stats { + color: #666; + font-size: 14px; + } + `, + events: { + 'click .database-card': (event = {}, instance = {}) => { + const database_name = event.currentTarget.getAttribute('data-database'); + window.location.href = `/joystickdb/databases/${database_name}`; + }, + }, + render: ({ data, each, when }) => { + return ` +
+

Databases

+

Select a database to browse its collections:

+ +
+ ${each(data?.databases || [], (database) => ` +
+
${database.name}
+
+ Collections: ${database.collections || 0} +
+
+ `)} +
+ + ${when(!data?.databases || data.databases.length === 0, ` +

No databases found.

+ `)} +
+ `; + }, +}); + +export default Databases; diff --git a/node/src/lib/joystickdb_gui/ui/pages/documents.js b/node/src/lib/joystickdb_gui/ui/pages/documents.js new file mode 100644 index 000000000..37de4305d --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/documents.js @@ -0,0 +1,255 @@ +import joystick from '@joystick.js/ui'; + +const Documents = joystick.component({ + data: async (api = {}, req = {}) => { + return { + documents: await api.get('joystickdb_get_documents', { + input: { + database: req?.params?.database, + collection: req?.params?.collection, + limit: 20, + }, + }), + database_name: req?.params?.database, + collection_name: req?.params?.collection, + }; + }, + state: { + selected_document: null, + page: 1, + }, + css: ` + .joystickdb-documents { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .document-actions { + display: flex; + gap: 10px; + margin-bottom: 20px; + } + + .btn { + padding: 8px 16px; + background: #007bff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + text-decoration: none; + font-size: 14px; + } + + .btn:hover { + background: #0056b3; + } + + .btn.secondary { + background: #6c757d; + } + + .btn.secondary:hover { + background: #5a6268; + } + + .documents-table { + width: 100%; + border-collapse: collapse; + background: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .documents-table th, + .documents-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; + } + + .documents-table th { + background: #f8f9fa; + font-weight: bold; + color: #333; + } + + .documents-table tr:hover { + background: #f8f9fa; + } + + .document-id { + font-family: monospace; + color: #007bff; + cursor: pointer; + } + + .document-preview { + max-width: 300px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-family: monospace; + font-size: 12px; + color: #666; + } + + .pagination { + display: flex; + justify-content: center; + gap: 10px; + margin-top: 20px; + } + + .page-btn { + padding: 8px 12px; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: 4px; + cursor: pointer; + text-decoration: none; + color: #333; + } + + .page-btn:hover, + .page-btn.active { + background: #007bff; + color: white; + } + + .document-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + } + + .document-content { + background: white; + padding: 20px; + border-radius: 8px; + max-width: 80%; + max-height: 80%; + overflow: auto; + position: relative; + } + + .close-modal { + position: absolute; + top: 10px; + right: 15px; + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + } + + .document-json { + background: #f8f9fa; + padding: 15px; + border-radius: 4px; + font-family: monospace; + white-space: pre-wrap; + word-break: break-all; + } + `, + events: { + 'click .document-id': (event = {}, instance = {}) => { + const document_id = event.target.textContent; + const document = instance.data.documents.find(doc => doc._id === document_id); + instance.set_state({ selected_document: document }); + }, + + 'click .close-modal': (event = {}, instance = {}) => { + instance.set_state({ selected_document: null }); + }, + + 'click .document-modal': (event = {}, instance = {}) => { + if (event.target === event.currentTarget) { + instance.set_state({ selected_document: null }); + } + }, + }, + render: ({ data, state, each, when }) => { + return ` +
+ + +

Documents in ${data?.collection_name || 'Unknown'}

+ +
+ + + Query Builder + +
+ + ${when(data?.documents && data.documents.length > 0, ` + + + + + + + + + + ${each(data.documents, (doc) => ` + + + + + + `)} + +
IDPreviewSize
+ ${doc._id} + +
+ ${JSON.stringify(doc).substring(0, 100)}... +
+
+ ${Math.ceil(JSON.stringify(doc).length / 1024)} KB +
+ `)} + + ${when(!data?.documents || data.documents.length === 0, ` +

No documents found in this collection.

+ `)} + + ${when(state.selected_document, ` +
+
+ +

Document Details

+
${JSON.stringify(state.selected_document, null, 2)}
+
+
+ `)} +
+ `; + }, +}); + +export default Documents; diff --git a/node/src/lib/joystickdb_gui/ui/pages/query.js b/node/src/lib/joystickdb_gui/ui/pages/query.js new file mode 100644 index 000000000..47d65bbbc --- /dev/null +++ b/node/src/lib/joystickdb_gui/ui/pages/query.js @@ -0,0 +1,304 @@ +import joystick from '@joystick.js/ui'; + +const Query = joystick.component({ + data: async (api = {}, req = {}) => { + return { + database_name: req?.params?.database, + collection_name: req?.params?.collection, + }; + }, + state: { + query: '{}', + results: null, + loading: false, + error: null, + query_history: [], + }, + css: ` + .joystickdb-query { + padding: 20px; + } + + .breadcrumb { + margin-bottom: 20px; + color: #666; + } + + .breadcrumb a { + color: #007bff; + text-decoration: none; + } + + .query-interface { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 20px; + } + + .query-input-section, + .query-results-section { + background: white; + border: 1px solid #ddd; + border-radius: 8px; + padding: 20px; + } + + .query-input-section h3, + .query-results-section h3 { + margin-top: 0; + color: #333; + } + + .query-textarea { + width: 100%; + height: 200px; + font-family: monospace; + font-size: 14px; + padding: 15px; + border: 1px solid #ccc; + border-radius: 4px; + resize: vertical; + } + + .query-actions { + display: flex; + gap: 10px; + margin-top: 15px; + } + + .btn { + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + } + + .btn.primary { + background: #007bff; + color: white; + } + + .btn.primary:hover { + background: #0056b3; + } + + .btn.primary:disabled { + background: #6c757d; + cursor: not-allowed; + } + + .btn.secondary { + background: #f8f9fa; + color: #333; + border: 1px solid #ddd; + } + + .btn.secondary:hover { + background: #e9ecef; + } + + .query-examples { + margin-top: 20px; + } + + .query-examples h4 { + margin-bottom: 10px; + color: #333; + } + + .example-query { + background: #f8f9fa; + padding: 10px; + border-radius: 4px; + font-family: monospace; + font-size: 12px; + margin-bottom: 10px; + cursor: pointer; + transition: background 0.2s; + } + + .example-query:hover { + background: #e9ecef; + } + + .results-container { + max-height: 400px; + overflow-y: auto; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: 4px; + } + + .results-json { + padding: 15px; + font-family: monospace; + font-size: 12px; + white-space: pre-wrap; + word-break: break-all; + } + + .error-message { + background: #f8d7da; + color: #721c24; + padding: 15px; + border-radius: 4px; + margin-top: 15px; + } + + .loading-spinner { + text-align: center; + padding: 40px; + color: #666; + } + + .query-stats { + display: flex; + justify-content: space-between; + margin-bottom: 10px; + font-size: 14px; + color: #666; + } + `, + events: { + 'submit .query-form': async (event = {}, instance = {}) => { + event.preventDefault(); + + const query = instance.state.query; + + if (!query.trim()) { + instance.set_state({ error: 'Query cannot be empty' }); + return; + } + + instance.set_state({ + loading: true, + error: null, + results: null + }); + + try { + const results = await api.set('joystickdb_run_query', { + input: { + database: instance.data.database_name, + collection: instance.data.collection_name, + query: query, + }, + }); + + instance.set_state({ + loading: false, + results: results, + query_history: [query, ...instance.state.query_history.slice(0, 9)], + }); + } catch (error) { + instance.set_state({ + loading: false, + error: error.message || 'Query execution failed', + }); + } + }, + + 'input .query-textarea': (event = {}, instance = {}) => { + instance.set_state({ query: event.target.value }); + }, + + 'click .example-query': (event = {}, instance = {}) => { + const example_query = event.target.textContent; + instance.set_state({ query: example_query }); + + const textarea = instance.DOMNode.querySelector('.query-textarea'); + if (textarea) { + textarea.value = example_query; + } + }, + + 'click .clear-query': (event = {}, instance = {}) => { + instance.set_state({ query: '{}', results: null, error: null }); + + const textarea = instance.DOMNode.querySelector('.query-textarea'); + if (textarea) { + textarea.value = '{}'; + } + }, + }, + render: ({ data, state, when, each }) => { + return ` +
+ + +

Query ${data?.collection_name || 'Unknown'}

+ +
+
+

Query Builder

+ +
+ + +
+ + +
+
+ +
+

Example Queries:

+
{}
+
{ "status": "active" }
+
{ "createdAt": { "$gte": "2024-01-01" } }
+
{ "name": { "$regex": "john", "$options": "i" } }
+
+
+ +
+

Results

+ + ${when(state.loading, ` +
+ Executing query... +
+ `)} + + ${when(state.error, ` +
+ Error: ${state.error} +
+ `)} + + ${when(state.results, ` +
+ Found: ${Array.isArray(state.results) ? state.results.length : 1} result(s) + Query time: ~${Math.random() * 100 | 0}ms +
+ +
+
${JSON.stringify(state.results, null, 2)}
+
+ `)} + + ${when(!state.loading && !state.error && !state.results, ` +

Execute a query to see results here.

+ `)} +
+
+
+ `; + }, +}); + +export default Query; diff --git a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js index 7274bc47e..28d3cdd48 100644 --- a/node/src/lib/joystickdb_gui/utils/resolve_component_path.js +++ b/node/src/lib/joystickdb_gui/utils/resolve_component_path.js @@ -27,17 +27,6 @@ const resolve_component_path = (relative_component_path = '') => { const should_use_absolute = relative_to_cwd.startsWith('node_modules/'); const final_path = should_use_absolute ? component_path : relative_to_cwd; - console.log(`JoystickDB path resolution:`, { - input: relative_component_path, - base_path, - component_path, - relative_to_cwd, - should_use_absolute, - final_path, - __dirname, - cwd: process.cwd() - }); - return final_path; }; From ce7b3d1720272407ccc05402f2df02e897552129 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 15:47:19 -0500 Subject: [PATCH 62/79] release 0.0.0-canary.2287 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index a0ebbf6bd..9483f3a93 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index 70ad0ee73..df82a9ff0 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2286", + "canary_version": "0.0.0-canary.2287", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 8b0e348c3bcab3410578bde7375a9b1d3bd9475a Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 15:47:22 -0500 Subject: [PATCH 63/79] release 0.0.0-canary.2287 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index ec6818408..e1fa01541 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 48a7f1d5f..849f66121 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2286", + "canary_version": "0.0.0-canary.2287", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From b5ffb8b7cc196e34d0e29709bc742cdd002f6b2f Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 15:47:25 -0500 Subject: [PATCH 64/79] release 0.0.0-canary.2287 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 1b47452d9..010183320 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index 56383d6e1..d7def1d1a 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2286", + "canary_version": "0.0.0-canary.2287", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From a4cb5f85a0b5099be717a196fb5890e5923b2d62 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 15:47:27 -0500 Subject: [PATCH 65/79] release 0.0.0-canary.2287 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 578704cf0..842b995e0 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index faafec733..766f7c146 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2286", + "canary_version": "0.0.0-canary.2287", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 0f2f923a5cd787e1163346225ee6bbaefa008366 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Sat, 4 Oct 2025 15:47:30 -0500 Subject: [PATCH 66/79] release 0.0.0-canary.2287 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 1063f51dd..15cc85816 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2286", + "version": "0.0.0-canary.2287", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index 1b4591939..bd0633cf4 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2286", + "canary_version": "0.0.0-canary.2287", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 7dee3a33d7c3cda840533bc8b96207facfcfe7b5 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 09:24:55 -0500 Subject: [PATCH 67/79] wip debugging joystickdb gui --- node/dist/app/middleware/render/index.js | 2 +- node/src/app/middleware/render/index.js | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/node/dist/app/middleware/render/index.js b/node/dist/app/middleware/render/index.js index b871e37ee..08b6a31bb 100644 --- a/node/dist/app/middleware/render/index.js +++ b/node/dist/app/middleware/render/index.js @@ -1 +1 @@ -import k from"fs";import i from"../generate_joystick_error_page.js";import j from"../../../lib/get_joystick_build_path.js";import b from"../../../lib/get_translations.js";import $ from"./get_url.js";import w from"../../ssr/index.js";import u from"../../../lib/strip_preceeding_slash.js";const{readFile:E}=k.promises,z=j(),A=(o,s,_,e={})=>{s.render=async(l="",t={})=>{const m=u(l),c=u(t?.layout),n=m,a=t?.layout?c:null;if(!process._joystick_components[n])return s.status(404).send(i({type:"page_not_found",path:`res.render('${n}')`,frame:null,stack:`A page component at the path ${n} could not be found.`}));if(a&&!process._joystick_components[a])return s.status(404).send(i({type:"layout_not_found",path:`res.render('${n}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const r=process._joystick_components[n],h=a?process._joystick_components[a]:null,p={...t?.props||{},theme:o?.cookies?.theme||"light"};a&&(p.page=r);const d=process._joystick_html,f=await b({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:l,req:o}),y=$(o),g=await w({api_schema:e?.options?.api,attributes:t?.attributes,base_html:d,component_options:{props:p,translations:f,url:y},component_to_render:h||r,escaping:t?.escaping,head:t?.head,render_component_path:m,render_layout_path:c,req:o,res:s,mod:t?.mod===!1?null:{in_use:!!e?.mod,css:e?.mod?.css||null,js:e?.mod?.js||null,theme:o?.cookies?.theme||e?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return s.status(200).send(g)},_()};var G=A;export{G as default}; +import j from"fs";import u from"../generate_joystick_error_page.js";import b from"../../../lib/get_joystick_build_path.js";import $ from"../../../lib/get_translations.js";import w from"./get_url.js";import A from"../../ssr/index.js";import r from"../../../lib/strip_preceeding_slash.js";const{readFile:O}=j.promises,x=b(),z=(s,e,_,a={})=>{e.render=async(n="",t={})=>{console.log(`res.render() called with: "${n}"`);const i=n.startsWith("/")?n:r(n),c=t?.layout?t.layout.startsWith("/")?t.layout:r(t.layout):null,o=i,l=c;if(console.log(`Looking for component at: "${o}"`),console.log(`Component exists: ${!!process._joystick_components[o]}`),console.log("Available JoystickDB components:",Object.keys(process._joystick_components).filter(k=>k.includes("joystickdb"))),!process._joystick_components[o])return e.status(404).send(u({type:"page_not_found",path:`res.render('${o}')`,frame:null,stack:`A page component at the path ${o} could not be found.`}));if(l&&!process._joystick_components[l])return e.status(404).send(u({type:"layout_not_found",path:`res.render('${o}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const m=process._joystick_components[o],h=l?process._joystick_components[l]:null,p={...t?.props||{},theme:s?.cookies?.theme||"light"};l&&(p.page=m);const y=process._joystick_html,d=await $({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${x}i18n`,render_component_path:n,req:s}),f=w(s),g=await A({api_schema:a?.options?.api,attributes:t?.attributes,base_html:y,component_options:{props:p,translations:d,url:f},component_to_render:h||m,escaping:t?.escaping,head:t?.head,render_component_path:i,render_layout_path:c,req:s,res:e,mod:t?.mod===!1?null:{in_use:!!a?.mod,css:a?.mod?.css||null,js:a?.mod?.js||null,theme:s?.cookies?.theme||a?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return e.status(200).send(g)},_()};var E=z;export{E as default}; diff --git a/node/src/app/middleware/render/index.js b/node/src/app/middleware/render/index.js index 9201fd6b1..bde8f17b4 100644 --- a/node/src/app/middleware/render/index.js +++ b/node/src/app/middleware/render/index.js @@ -14,12 +14,21 @@ const render_middleware = (req, res, next, app_instance = {}) => { // NOTE: Set res.render here so we have access to req, res, and // app_instance objects inside of the definition. res.render = async (render_component_path = '', render_options = {}) => { - // NOTE: Safety mechanism. Don't punish a developer if the path they pass to res.render() - // has a forward slash prepended, just strip it for them. - const sanitized_render_component_path = strip_preceeding_slash(render_component_path); - const sanitized_render_layout_path = strip_preceeding_slash(render_options?.layout); + console.log(`res.render() called with: "${render_component_path}"`); + + // NOTE: Don't strip leading slash from absolute paths (needed for production installs) + const is_absolute_path = render_component_path.startsWith('/'); + const sanitized_render_component_path = is_absolute_path ? render_component_path : strip_preceeding_slash(render_component_path); + const sanitized_render_layout_path = render_options?.layout ? + (render_options.layout.startsWith('/') ? render_options.layout : strip_preceeding_slash(render_options.layout)) + : null; + const component_path = sanitized_render_component_path; - const layout_path = render_options?.layout ? sanitized_render_layout_path : null; + const layout_path = sanitized_render_layout_path; + + console.log(`Looking for component at: "${component_path}"`); + console.log(`Component exists: ${!!process._joystick_components[component_path]}`); + console.log(`Available JoystickDB components:`, Object.keys(process._joystick_components).filter(key => key.includes('joystickdb'))); if (!process._joystick_components[component_path]) { return res.status(404).send( From 294a61866196f05867b6883230f85f3f05323fa2 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 09:25:08 -0500 Subject: [PATCH 68/79] release 0.0.0-canary.2288 --- cli/package-lock.json | 4 ++-- cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index 9483f3a93..850b5b757 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/cli", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/cli/package.json b/cli/package.json index df82a9ff0..38626fcec 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/cli", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2287", + "canary_version": "0.0.0-canary.2288", "description": "The CLI for Joystick.", "main": "dist/index.js", "bin": { From 56c3fd7469e1b338c95d40177a430a05cbb046fd Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 09:25:11 -0500 Subject: [PATCH 69/79] release 0.0.0-canary.2288 --- db/package-lock.json | 4 ++-- db/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index e1fa01541..adbd4f03c 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/db", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@joystick.js/db", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.879.0", diff --git a/db/package.json b/db/package.json index 849f66121..6792d73ee 100644 --- a/db/package.json +++ b/db/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/db", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2287", + "canary_version": "0.0.0-canary.2288", "description": "JoystickDB - A minimalist database server for the Joystick framework", "main": "./dist/server/index.js", "scripts": { From 31d2d1a1dabd51f031835647cfab5d69ffa58fcc Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 09:25:15 -0500 Subject: [PATCH 70/79] release 0.0.0-canary.2288 --- node/package-lock.json | 4 ++-- node/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/node/package-lock.json b/node/package-lock.json index 010183320..4d3e78cd9 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/node", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/node", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "license": "SAUCR", "dependencies": { "@aws-sdk/client-s3": "^3.478.0", diff --git a/node/package.json b/node/package.json index d7def1d1a..5975c3eaa 100644 --- a/node/package.json +++ b/node/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/node", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2287", + "canary_version": "0.0.0-canary.2288", "description": "The Node.js framework for Joystick.", "main": "./dist/index.js", "scripts": { From f0429e19910dc26f560afd41d5e93785d0434fb3 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 09:25:17 -0500 Subject: [PATCH 71/79] release 0.0.0-canary.2288 --- test/package-lock.json | 4 ++-- test/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/package-lock.json b/test/package-lock.json index 842b995e0..36a25db76 100644 --- a/test/package-lock.json +++ b/test/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/test", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/test", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "license": "ISC", "dependencies": { "ava": "^6.2.0", diff --git a/test/package.json b/test/package.json index 766f7c146..5b69a8532 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/test", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2287", + "canary_version": "0.0.0-canary.2288", "description": "The testing framework for Joystick.", "main": "./dist/index.js", "scripts": { From 3dc788bf7f9ffb257f3db9ca6021ec6ce351de5c Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 09:25:20 -0500 Subject: [PATCH 72/79] release 0.0.0-canary.2288 --- ui/package-lock.json | 4 ++-- ui/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 15cc85816..b7cfb3584 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@joystick.js/ui", - "version": "0.0.0-canary.2287", + "version": "0.0.0-canary.2288", "license": "SAUCR", "dependencies": { "js-cookie": "^3.0.5", diff --git a/ui/package.json b/ui/package.json index bd0633cf4..8cd09cc69 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,7 +2,7 @@ "name": "@joystick.js/ui", "type": "module", "version": "1.0.0-rc.3", - "canary_version": "0.0.0-canary.2287", + "canary_version": "0.0.0-canary.2288", "description": "The UI framework for Joystick.", "main": "./dist/index.js", "scripts": { From 8363987522eea09f67e170e283d69c4dd6180a68 Mon Sep 17 00:00:00 2001 From: Ryan Glover Date: Mon, 6 Oct 2025 10:12:08 -0500 Subject: [PATCH 73/79] wip doing joystickdb gui styles from scratch --- node/dist/app/middleware/render/index.js | 2 +- .../joystickdb_gui/ui/pages/admin/index.js | 134 +------ .../ui/pages/admin/replication.js | 231 ++---------- .../joystickdb_gui/ui/pages/admin/stats.js | 303 ++------------- .../joystickdb_gui/ui/pages/admin/users.js | 161 +------- .../joystickdb_gui/ui/pages/collections.js | 95 +---- .../lib/joystickdb_gui/ui/pages/databases.js | 63 +--- .../lib/joystickdb_gui/ui/pages/documents.js | 207 ++-------- .../dist/lib/joystickdb_gui/ui/pages/query.js | 256 +++---------- node/src/app/middleware/render/index.js | 6 - .../joystickdb_gui/ui/pages/admin/index.js | 139 ++----- .../ui/pages/admin/replication.js | 236 ++---------- .../joystickdb_gui/ui/pages/admin/stats.js | 356 ++---------------- .../joystickdb_gui/ui/pages/admin/users.js | 174 +-------- .../joystickdb_gui/ui/pages/collections.js | 98 +---- .../lib/joystickdb_gui/ui/pages/databases.js | 66 +--- .../lib/joystickdb_gui/ui/pages/documents.js | 209 +--------- node/src/lib/joystickdb_gui/ui/pages/query.js | 287 +++----------- 18 files changed, 386 insertions(+), 2637 deletions(-) diff --git a/node/dist/app/middleware/render/index.js b/node/dist/app/middleware/render/index.js index 08b6a31bb..f449b5310 100644 --- a/node/dist/app/middleware/render/index.js +++ b/node/dist/app/middleware/render/index.js @@ -1 +1 @@ -import j from"fs";import u from"../generate_joystick_error_page.js";import b from"../../../lib/get_joystick_build_path.js";import $ from"../../../lib/get_translations.js";import w from"./get_url.js";import A from"../../ssr/index.js";import r from"../../../lib/strip_preceeding_slash.js";const{readFile:O}=j.promises,x=b(),z=(s,e,_,a={})=>{e.render=async(n="",t={})=>{console.log(`res.render() called with: "${n}"`);const i=n.startsWith("/")?n:r(n),c=t?.layout?t.layout.startsWith("/")?t.layout:r(t.layout):null,o=i,l=c;if(console.log(`Looking for component at: "${o}"`),console.log(`Component exists: ${!!process._joystick_components[o]}`),console.log("Available JoystickDB components:",Object.keys(process._joystick_components).filter(k=>k.includes("joystickdb"))),!process._joystick_components[o])return e.status(404).send(u({type:"page_not_found",path:`res.render('${o}')`,frame:null,stack:`A page component at the path ${o} could not be found.`}));if(l&&!process._joystick_components[l])return e.status(404).send(u({type:"layout_not_found",path:`res.render('${o}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const m=process._joystick_components[o],h=l?process._joystick_components[l]:null,p={...t?.props||{},theme:s?.cookies?.theme||"light"};l&&(p.page=m);const y=process._joystick_html,d=await $({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${x}i18n`,render_component_path:n,req:s}),f=w(s),g=await A({api_schema:a?.options?.api,attributes:t?.attributes,base_html:y,component_options:{props:p,translations:d,url:f},component_to_render:h||m,escaping:t?.escaping,head:t?.head,render_component_path:i,render_layout_path:c,req:s,res:e,mod:t?.mod===!1?null:{in_use:!!a?.mod,css:a?.mod?.css||null,js:a?.mod?.js||null,theme:s?.cookies?.theme||a?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return e.status(200).send(g)},_()};var E=z;export{E as default}; +import k from"fs";import p from"../generate_joystick_error_page.js";import j from"../../../lib/get_joystick_build_path.js";import b from"../../../lib/get_translations.js";import $ from"./get_url.js";import w from"../../ssr/index.js";import r from"../../../lib/strip_preceeding_slash.js";const{readFile:E}=k.promises,z=j(),A=(o,s,_,a={})=>{s.render=async(l="",t={})=>{const m=l.startsWith("/")?l:r(l),c=t?.layout?t.layout.startsWith("/")?t.layout:r(t.layout):null,e=m,n=c;if(!process._joystick_components[e])return s.status(404).send(p({type:"page_not_found",path:`res.render('${e}')`,frame:null,stack:`A page component at the path ${e} could not be found.`}));if(n&&!process._joystick_components[n])return s.status(404).send(p({type:"layout_not_found",path:`res.render('${e}', { layout: '${c}' })`,frame:null,stack:`A layout component at the path ${t?.layout} could not be found.`}));const i=process._joystick_components[e],h=n?process._joystick_components[n]:null,u={...t?.props||{},theme:o?.cookies?.theme||"light"};n&&(u.page=i);const y=process._joystick_html,d=await b({language_files:process._joystick_translations?.normal?.files||[],language_files_path:process._joystick_translations?.normal?.path||`${z}i18n`,render_component_path:l,req:o}),f=$(o),g=await w({api_schema:a?.options?.api,attributes:t?.attributes,base_html:y,component_options:{props:u,translations:d,url:f},component_to_render:h||i,escaping:t?.escaping,head:t?.head,render_component_path:m,render_layout_path:c,req:o,res:s,mod:t?.mod===!1?null:{in_use:!!a?.mod,css:a?.mod?.css||null,js:a?.mod?.js||null,theme:o?.cookies?.theme||a?.options?.mod?.default_theme||"light",components_in_use:t?.mod?.components}});return s.status(200).send(g)},_()};var G=A;export{G as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js index 802008f15..c26b25196 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/index.js @@ -1,123 +1,25 @@ -import d from"@joystick.js/ui";const s=d.component({data:async(a={})=>({stats:await a.get("joystickdb_get_stats")}),css:` - .joystickdb-admin { - padding: 20px; - } - - .admin-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 20px; - margin: 20px 0; - } - - .admin-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - transition: transform 0.2s; - text-align: center; - } - - .admin-card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); - } - - .admin-card-icon { - font-size: 2rem; - margin-bottom: 10px; - color: #007bff; - } - - .admin-card-title { - font-size: 18px; - font-weight: bold; - margin-bottom: 10px; - color: #333; - } - - .admin-card-description { - color: #666; - font-size: 14px; - } - - .stats-overview { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 20px; - margin-bottom: 30px; - } - - .stat-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - text-align: center; - } - - .stat-value { - font-size: 2rem; - font-weight: bold; - color: #007bff; - margin-bottom: 5px; - } - - .stat-label { - color: #666; - font-size: 14px; - } - `,events:{"click .admin-card":(a={},t={})=>{const i=a.currentTarget.getAttribute("data-link");i&&(window.location.href=i)}},render:({data:a,each:t})=>` -
+import s from"@joystick.js/ui";const a=s.component({data:async(t={})=>({stats:await t.get("joystickdb_get_stats")}),events:{"click a":(t={},e={})=>{const i=t.target.getAttribute("href");i&&(window.location.href=i)}},render:({data:t})=>(console.log("Admin dashboard data:",t),` +

JoystickDB Administration

Manage your JoystickDB instance settings and monitor performance.

-
-
-
${a?.stats?.total_databases||0}
-
Databases
-
-
-
${a?.stats?.total_collections||0}
-
Collections
-
-
-
${a?.stats?.total_documents||0}
-
Documents
-
-
-
${a?.stats?.storage_size||"0 B"}
-
Storage Used
-
+
+

Statistics

+
    +
  • Databases: ${t?.stats?.total_databases||0}
  • +
  • Collections: ${t?.stats?.total_collections||0}
  • +
  • Documents: ${t?.stats?.total_documents||0}
  • +
  • Storage Used: ${t?.stats?.storage_size||"0 B"}
  • +
-
-
-
\u{1F465}
-
User Management
-
- Manage JoystickDB users, roles, and permissions -
-
- -
-
\u{1F504}
-
Replication
-
- Configure database replication and clustering -
-
- -
-
\u{1F4CA}
-
Statistics
-
- View detailed performance metrics and analytics -
-
+
+

Admin Tools

+
    +
  • User Management - Manage JoystickDB users, roles, and permissions
  • +
  • Replication - Configure database replication and clustering
  • +
  • Statistics - View detailed performance metrics and analytics
  • +
- `});var r=s;export{r as default}; + `)});var o=a;export{o as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js index 2945e22fa..d29628a8c 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/replication.js @@ -1,217 +1,52 @@ -import n from"@joystick.js/ui";const o=n.component({data:async(a={})=>({replication_status:await a.get("joystickdb_get_replication_status")}),state:{adding_replica:!1},css:` - .joystickdb-admin-replication { - padding: 20px; - } - - .breadcrumb { - margin-bottom: 20px; - color: #666; - } - - .breadcrumb a { - color: #007bff; - text-decoration: none; - } - - .replication-overview { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - margin-bottom: 20px; - } - - .status-indicator { - display: inline-block; - padding: 4px 12px; - border-radius: 12px; - font-size: 12px; - font-weight: bold; - margin-bottom: 15px; - } - - .status-indicator.enabled { - background: #d4edda; - color: #155724; - } - - .status-indicator.disabled { - background: #f8d7da; - color: #721c24; - } - - .replica-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 20px; - margin-top: 20px; - } - - .replica-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - - .replica-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 15px; - } - - .replica-name { - font-size: 16px; - font-weight: bold; - color: #333; - } - - .replica-status { - padding: 4px 8px; - border-radius: 8px; - font-size: 12px; - font-weight: bold; - } - - .replica-status.primary { - background: #e3f2fd; - color: #1976d2; - } - - .replica-status.secondary { - background: #f3e5f5; - color: #7b1fa2; - } - - .replica-status.healthy { - background: #e8f5e8; - color: #2e7d32; - } - - .replica-status.unhealthy { - background: #ffebee; - color: #c62828; - } - - .replica-details { - font-size: 14px; - color: #666; - } - - .detail-row { - display: flex; - justify-content: space-between; - margin-bottom: 8px; - } - - .btn { - padding: 8px 16px; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - text-decoration: none; - } - - .btn.primary { - background: #007bff; - color: white; - } - - .btn.danger { - background: #dc3545; - color: white; - } - - .btn.success { - background: #28a745; - color: white; - } - - .replication-actions { - display: flex; - gap: 10px; - margin-bottom: 20px; - } - `,events:{"click .add-replica":(a={},t={})=>{t.set_state({adding_replica:!0})},"click .enable-replication":async(a={},t={})=>{try{await api.set("joystickdb_enable_replication"),t.data.refetch()}catch(s){alert("Failed to enable replication: "+s.message)}}},render:({data:a,state:t,each:s,when:e})=>` -
- +import l from"@joystick.js/ui";const n=l.component({data:async(i={})=>({replication_status:await i.get("joystickdb_get_replication_status")}),render:({data:i,each:e,when:a})=>(console.log("Admin replication data:",i),` +
+

Admin > Replication

Replication Management

Configure and monitor database replication settings.

-
+

Replication Status

- - ${a?.replication_status?.enabled?"Enabled":"Disabled"} - +

Status: ${i?.replication_status?.enabled?"Enabled":"Disabled"}

- ${e(a?.replication_status?.enabled,` -
- Primary Node: - ${a.replication_status.primary_node||"Unknown"} -
-
- Replica Set: - ${a.replication_status.replica_set||"Unknown"} -
-
- Total Replicas: - ${a.replication_status.replica_count||0} -
+ ${a(i?.replication_status?.enabled,` +
    +
  • Primary Node: ${i.replication_status.primary_node||"Unknown"}
  • +
  • Replica Set: ${i.replication_status.replica_set||"Unknown"}
  • +
  • Total Replicas: ${i.replication_status.replica_count||0}
  • +
`)}
-
- ${e(!a?.replication_status?.enabled,` - +

+ ${a(!i?.replication_status?.enabled,` + `)} - ${e(a?.replication_status?.enabled,` - - + ${a(i?.replication_status?.enabled,` + + `)} -

+

- ${e(a?.replication_status?.enabled&&a?.replication_status?.replicas,` -
- ${s(a.replication_status.replicas,i=>` -
-
-
${i.name}
-
${i.type}
-
- -
-
- Host: - ${i.host} -
-
- Port: - ${i.port} -
-
- Health: - - ${i.healthy?"Healthy":"Unhealthy"} - -
-
- Lag: - ${i.lag||"0"}ms -
-
+ ${a(i?.replication_status?.enabled&&i?.replication_status?.replicas,` +
+

Replicas

+ ${e(i.replication_status.replicas,t=>` +
+

${t.name} (${t.type})

+
    +
  • Host: ${t.host}
  • +
  • Port: ${t.port}
  • +
  • Health: ${t.healthy?"Healthy":"Unhealthy"}
  • +
  • Lag: ${t.lag||"0"}ms
  • +
`)}
`)} - ${e(!a?.replication_status?.enabled,` -
+ ${a(!i?.replication_status?.enabled,` +

About Replication

Database replication provides high availability and data redundancy by maintaining copies of your data across multiple servers.

    @@ -223,4 +58,4 @@ import n from"@joystick.js/ui";const o=n.component({data:async(a={})=>({replicat
`)}
- `});var d=o;export{d as default}; + `)});var s=n;export{s as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js index 28ff502a4..f5b6cae31 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/stats.js @@ -1,268 +1,36 @@ -import o from"@joystick.js/ui";const l=o.component({data:async(t={})=>({detailed_stats:await t.get("joystickdb_get_detailed_stats")}),state:{refresh_interval:null,auto_refresh:!1},lifecycle:{on_mount:(t={})=>{t.state.auto_refresh&&t.methods.start_auto_refresh()},on_before_unmount:(t={})=>{t.state.refresh_interval&&clearInterval(t.state.refresh_interval)}},methods:{start_auto_refresh:(t={})=>{const e=setInterval(()=>{t.data.refetch()},5e3);t.set_state({refresh_interval:e})},stop_auto_refresh:(t={})=>{t.state.refresh_interval&&(clearInterval(t.state.refresh_interval),t.set_state({refresh_interval:null}))}},css:` - .joystickdb-admin-stats { - padding: 20px; - } - - .breadcrumb { - margin-bottom: 20px; - color: #666; - } - - .breadcrumb a { - color: #007bff; - text-decoration: none; - } - - .stats-controls { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - } - - .refresh-controls { - display: flex; - gap: 10px; - align-items: center; - } - - .btn { - padding: 8px 16px; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - } - - .btn.primary { - background: #007bff; - color: white; - } - - .btn.secondary { - background: #6c757d; - color: white; - } - - .btn.success { - background: #28a745; - color: white; - } - - .toggle-switch { - position: relative; - width: 50px; - height: 24px; - background: #ccc; - border-radius: 12px; - cursor: pointer; - transition: background 0.3s; - } - - .toggle-switch.active { - background: #28a745; - } - - .toggle-slider { - position: absolute; - top: 2px; - left: 2px; - width: 20px; - height: 20px; - background: white; - border-radius: 50%; - transition: transform 0.3s; - } - - .toggle-switch.active .toggle-slider { - transform: translateX(26px); - } - - .stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 20px; - margin-bottom: 30px; - } - - .stat-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - text-align: center; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - - .stat-value { - font-size: 2rem; - font-weight: bold; - color: #007bff; - margin-bottom: 5px; - } - - .stat-label { - color: #666; - font-size: 14px; - margin-bottom: 5px; - } - - .stat-change { - font-size: 12px; - padding: 2px 6px; - border-radius: 8px; - } - - .stat-change.positive { - background: #d4edda; - color: #155724; - } - - .stat-change.negative { - background: #f8d7da; - color: #721c24; - } - - .performance-charts { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - margin-top: 20px; - } - - .chart-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - - .chart-title { - font-size: 16px; - font-weight: bold; - margin-bottom: 15px; - color: #333; - } - - .chart-placeholder { - height: 200px; - background: #f8f9fa; - border: 2px dashed #dee2e6; - display: flex; - align-items: center; - justify-content: center; - color: #6c757d; - border-radius: 4px; - } - - .detail-table { - width: 100%; - border-collapse: collapse; - margin-top: 20px; - } - - .detail-table th, - .detail-table td { - padding: 12px; - text-align: left; - border-bottom: 1px solid #eee; - } - - .detail-table th { - background: #f8f9fa; - font-weight: bold; - color: #333; - } - - .last-updated { - font-size: 12px; - color: #666; - margin-top: 10px; - } - `,events:{"click .refresh-stats":(t={},e={})=>{e.data.refetch()},"click .toggle-auto-refresh":(t={},e={})=>{const s=!e.state.auto_refresh;e.set_state({auto_refresh:s}),s?e.methods.start_auto_refresh():e.methods.stop_auto_refresh()},"click .export-stats":(t={},e={})=>{const s=e.data.detailed_stats,i=JSON.stringify(s,null,2),a="data:application/json;charset=utf-8,"+encodeURIComponent(i),d=`joystickdb-stats-${new Date().toISOString().split("T")[0]}.json`,r=document.createElement("a");r.setAttribute("href",a),r.setAttribute("download",d),r.click()}},render:({data:t,state:e,each:s,when:i})=>` -
- +import o from"@joystick.js/ui";const d=o.component({data:async(t={})=>({detailed_stats:await t.get("joystickdb_get_detailed_stats")}),events:{"click .refresh-stats":(t={},s={})=>{s.data.refetch()}},render:({data:t,each:s,when:i})=>(console.log("Admin stats data:",t),` +
+

Admin > Statistics

-
-

Performance Statistics

- -
- -
-
-
- - -
-
+

Performance Statistics

-
-
-
${t?.detailed_stats?.operations?.reads||0}
-
Read Operations
-
+${t?.detailed_stats?.operations?.reads_change||0}%
-
- -
-
${t?.detailed_stats?.operations?.writes||0}
-
Write Operations
-
+${t?.detailed_stats?.operations?.writes_change||0}%
-
- -
-
${t?.detailed_stats?.performance?.avg_response_time||0}ms
-
Avg Response Time
-
- ${t?.detailed_stats?.performance?.response_time_change||0}ms -
-
- -
-
${t?.detailed_stats?.connections?.active||0}
-
Active Connections
-
+${t?.detailed_stats?.connections?.active_change||0}
-
- -
-
${t?.detailed_stats?.memory?.used||"0 MB"}
-
Memory Used
-
- ${t?.detailed_stats?.memory?.usage_change||0}% -
-
- -
-
${t?.detailed_stats?.storage?.disk_usage||"0%"}
-
Disk Usage
-
- ${t?.detailed_stats?.storage?.disk_change||0}% -
-
+

+ + +

+ +
+

Performance Metrics

+
    +
  • Read Operations: ${t?.detailed_stats?.operations?.reads||0}
  • +
  • Write Operations: ${t?.detailed_stats?.operations?.writes||0}
  • +
  • Avg Response Time: ${t?.detailed_stats?.performance?.avg_response_time||0}ms
  • +
  • Active Connections: ${t?.detailed_stats?.connections?.active||0}
  • +
  • Memory Used: ${t?.detailed_stats?.memory?.used||"0 MB"}
  • +
  • Disk Usage: ${t?.detailed_stats?.storage?.disk_usage||"0%"}
  • +
-
-
-
Operations Over Time
-
- \u{1F4CA} Chart visualization would appear here -
-
- -
-
Response Time Trends
-
- \u{1F4C8} Response time chart would appear here -
-
+
+

Charts

+

\u{1F4CA} Operations Over Time (Chart placeholder)

+

\u{1F4C8} Response Time Trends (Chart placeholder)

${i(t?.detailed_stats?.slow_queries,` -
-
Slow Queries
- +
+

Slow Queries

+
@@ -272,14 +40,12 @@ import o from"@joystick.js/ui";const l=o.component({data:async(t={})=>({detailed - ${s(t.detailed_stats.slow_queries,a=>` + ${s(t.detailed_stats.slow_queries,e=>` - - - - + + + + `)} @@ -287,9 +53,6 @@ import o from"@joystick.js/ui";const l=o.component({data:async(t={})=>({detailed `)} -
- Last updated: ${new Date().toLocaleString()} - ${i(e.auto_refresh,"(Auto-refreshing every 5 seconds)")} -
+

Last updated: ${new Date().toLocaleString()}

- `});var n=l;export{n as default}; + `)});var r=d;export{r as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js b/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js index 363f33d98..ee3acefb7 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/admin/users.js @@ -1,136 +1,17 @@ -import d from"@joystick.js/ui";const n=d.component({data:async(e={})=>({users:await e.get("joystickdb_get_users")}),state:{selected_user:null,editing_user:!1},css:` - .joystickdb-admin-users { - padding: 20px; - } - - .breadcrumb { - margin-bottom: 20px; - color: #666; - } - - .breadcrumb a { - color: #007bff; - text-decoration: none; - } - - .user-actions { - display: flex; - gap: 10px; - margin-bottom: 20px; - } - - .btn { - padding: 8px 16px; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - text-decoration: none; - } - - .btn.primary { - background: #007bff; - color: white; - } - - .btn.secondary { - background: #6c757d; - color: white; - } - - .users-table { - width: 100%; - border-collapse: collapse; - background: white; - border-radius: 8px; - overflow: hidden; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - - .users-table th, - .users-table td { - padding: 12px; - text-align: left; - border-bottom: 1px solid #eee; - } - - .users-table th { - background: #f8f9fa; - font-weight: bold; - color: #333; - } - - .users-table tr:hover { - background: #f8f9fa; - } - - .user-status { - padding: 4px 8px; - border-radius: 12px; - font-size: 12px; - font-weight: bold; - } - - .user-status.active { - background: #d4edda; - color: #155724; - } - - .user-status.inactive { - background: #f8d7da; - color: #721c24; - } - - .user-role { - background: #e3f2fd; - color: #1976d2; - padding: 2px 6px; - border-radius: 8px; - font-size: 11px; - margin-right: 4px; - } - - .action-btn { - background: none; - border: 1px solid #007bff; - color: #007bff; - padding: 4px 8px; - border-radius: 4px; - cursor: pointer; - font-size: 12px; - margin-right: 5px; - } - - .action-btn:hover { - background: #007bff; - color: white; - } - - .action-btn.danger { - border-color: #dc3545; - color: #dc3545; - } - - .action-btn.danger:hover { - background: #dc3545; - color: white; - } - `,events:{"click .view-user":(e={},r={})=>{const s=e.target.getAttribute("data-user-id"),o=r.data.users.find(t=>t._id===s);r.set_state({selected_user:o})},"click .edit-user":(e={},r={})=>{const s=e.target.getAttribute("data-user-id"),o=r.data.users.find(t=>t._id===s);r.set_state({selected_user:o,editing_user:!0})}},render:({data:e,state:r,each:s,when:o})=>` -
- +import o from"@joystick.js/ui";const d=o.component({data:async(e={})=>({users:await e.get("joystickdb_get_users")}),render:({data:e,each:n,when:s})=>(console.log("Admin users data:",e),` +
+

Admin > Users

User Management

Manage JoystickDB users and their permissions.

- +

+ + +

- ${o(e?.users&&e.users.length>0,` -
Query
- ${JSON.stringify(a.query).substring(0,50)}... - ${a.collection}${a.avg_response_time}ms${a.count}${JSON.stringify(e.query).substring(0,50)}...${e.collection}${e.avg_response_time}ms${e.count}
+ ${s(e?.users&&e.users.length>0,` +
@@ -142,25 +23,17 @@ import d from"@joystick.js/ui";const n=d.component({data:async(e={})=>({users:aw - ${s(e.users,t=>` + ${n(e.users,t=>` - - + + `)} @@ -168,8 +41,8 @@ import d from"@joystick.js/ui";const n=d.component({data:async(e={})=>({users:aw
Username
${t.username} ${t.email} - ${s(t.roles||["user"],a=>` - ${a} - `)} - - - ${t.active?"Active":"Inactive"} - - ${(t.roles||["user"]).join(", ")}${t.active?"Active":"Inactive"} ${t.last_login?new Date(t.last_login).toLocaleDateString():"Never"} - - - + + +
`)} - ${o(!e?.users||e.users.length===0,` + ${s(!e?.users||e.users.length===0,`

No users found.

`)}
- `});var c=n;export{c as default}; + `)});var i=d;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/collections.js b/node/dist/lib/joystickdb_gui/ui/pages/collections.js index abd11ce02..6e29e654a 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/collections.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/collections.js @@ -1,90 +1,23 @@ -import n from"@joystick.js/ui";const e=n.component({data:async(a={},o={})=>({collections:await a.get("joystickdb_get_collections",{input:{database:o?.params?.database}}),database_name:o?.params?.database}),css:` - .joystickdb-collections { - padding: 20px; - } - - .breadcrumb { - margin-bottom: 20px; - color: #666; - } - - .breadcrumb a { - color: #007bff; - text-decoration: none; - } - - .collections-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 20px; - margin-top: 20px; - } - - .collection-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - transition: transform 0.2s; - } - - .collection-card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); - } - - .collection-name { - font-size: 18px; - font-weight: bold; - margin-bottom: 10px; - color: #333; - } - - .collection-stats { - color: #666; - font-size: 14px; - } - - .stat-row { - display: flex; - justify-content: space-between; - margin-bottom: 5px; - } - `,events:{"click .collection-card":(a={},o={})=>{const s=a.currentTarget.getAttribute("data-collection"),t=o.data?.database_name;window.location.href=`/joystickdb/databases/${t}/${s}`}},render:({data:a,each:o,when:s})=>` -
- +import s from"@joystick.js/ui";const o=s.component({data:async(a={},t={})=>({collections:await a.get("joystickdb_get_collections",{input:{database:t?.params?.database}}),database_name:t?.params?.database}),events:{"click a":(a={},t={})=>{const n=a.target.getAttribute("data-collection"),e=t.data?.database_name;n&&e&&(window.location.href=`/joystickdb/databases/${e}/${n}`)}},render:({data:a,each:t,when:n})=>(console.log("Collections data:",a),` +
+

Databases > ${a?.database_name||"Unknown"}

Collections in ${a?.database_name||"Unknown"}

Select a collection to browse its documents:

-
- ${o(a?.collections||[],t=>` -
-
${t.name}
-
-
- Documents: - ${t.document_count||0} -
-
- Size: - ${t.size||"0 B"} -
-
- Indexes: - ${t.index_count||0} -
-
-
+
+ - ${s(!a?.collections||a.collections.length===0,` + ${n(!a?.collections||a.collections.length===0,`

No collections found in this database.

`)}
- `});var i=e;export{i as default}; + `)});var i=o;export{i as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/databases.js b/node/dist/lib/joystickdb_gui/ui/pages/databases.js index 5c05dae0a..f33d47b68 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/databases.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/databases.js @@ -1,59 +1,20 @@ -import d from"@joystick.js/ui";const o=d.component({data:async(a={})=>({databases:await a.get("joystickdb_get_databases")}),css:` - .joystickdb-databases { - padding: 20px; - } - - .database-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 20px; - margin-top: 20px; - } - - .database-card { - background: white; - border: 1px solid #ddd; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - cursor: pointer; - transition: transform 0.2s; - } - - .database-card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); - } - - .database-name { - font-size: 18px; - font-weight: bold; - margin-bottom: 10px; - color: #333; - } - - .database-stats { - color: #666; - font-size: 14px; - } - `,events:{"click .database-card":(a={},e={})=>{const s=a.currentTarget.getAttribute("data-database");window.location.href=`/joystickdb/databases/${s}`}},render:({data:a,each:e,when:s})=>` -
+import o from"@joystick.js/ui";const n=o.component({data:async(a={})=>({databases:await a.get("joystickdb_get_databases")}),events:{"click a":(a={},s={})=>{const t=a.target.getAttribute("data-database");t&&(window.location.href=`/joystickdb/databases/${t}`)}},render:({data:a,each:s,when:t})=>(console.log("Databases data:",a),` +

Databases

Select a database to browse its collections:

-
- ${e(a?.databases||[],t=>` -
-
${t.name}
-
- Collections: ${t.collections||0} -
-
+
+ - ${s(!a?.databases||a.databases.length===0,` + ${t(!a?.databases||a.databases.length===0,`

No databases found.

`)}
- `});var i=o;export{i as default}; + `)});var c=n;export{c as default}; diff --git a/node/dist/lib/joystickdb_gui/ui/pages/documents.js b/node/dist/lib/joystickdb_gui/ui/pages/documents.js index d83bf8762..65ef44a43 100644 --- a/node/dist/lib/joystickdb_gui/ui/pages/documents.js +++ b/node/dist/lib/joystickdb_gui/ui/pages/documents.js @@ -1,173 +1,20 @@ -import a from"@joystick.js/ui";const c=a.component({data:async(t={},e={})=>({documents:await t.get("joystickdb_get_documents",{input:{database:e?.params?.database,collection:e?.params?.collection,limit:20}}),database_name:e?.params?.database,collection_name:e?.params?.collection}),state:{selected_document:null,page:1},css:` - .joystickdb-documents { - padding: 20px; - } - - .breadcrumb { - margin-bottom: 20px; - color: #666; - } - - .breadcrumb a { - color: #007bff; - text-decoration: none; - } - - .document-actions { - display: flex; - gap: 10px; - margin-bottom: 20px; - } - - .btn { - padding: 8px 16px; - background: #007bff; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - text-decoration: none; - font-size: 14px; - } - - .btn:hover { - background: #0056b3; - } - - .btn.secondary { - background: #6c757d; - } - - .btn.secondary:hover { - background: #5a6268; - } - - .documents-table { - width: 100%; - border-collapse: collapse; - background: white; - border-radius: 8px; - overflow: hidden; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - - .documents-table th, - .documents-table td { - padding: 12px; - text-align: left; - border-bottom: 1px solid #eee; - } - - .documents-table th { - background: #f8f9fa; - font-weight: bold; - color: #333; - } - - .documents-table tr:hover { - background: #f8f9fa; - } - - .document-id { - font-family: monospace; - color: #007bff; - cursor: pointer; - } - - .document-preview { - max-width: 300px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-family: monospace; - font-size: 12px; - color: #666; - } - - .pagination { - display: flex; - justify-content: center; - gap: 10px; - margin-top: 20px; - } - - .page-btn { - padding: 8px 12px; - background: #f8f9fa; - border: 1px solid #ddd; - border-radius: 4px; - cursor: pointer; - text-decoration: none; - color: #333; - } - - .page-btn:hover, - .page-btn.active { - background: #007bff; - color: white; - } - - .document-modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; - } - - .document-content { - background: white; - padding: 20px; - border-radius: 8px; - max-width: 80%; - max-height: 80%; - overflow: auto; - position: relative; - } - - .close-modal { - position: absolute; - top: 10px; - right: 15px; - background: none; - border: none; - font-size: 24px; - cursor: pointer; - color: #666; - } - - .document-json { - background: #f8f9fa; - padding: 15px; - border-radius: 4px; - font-family: monospace; - white-space: pre-wrap; - word-break: break-all; - } - `,events:{"click .document-id":(t={},e={})=>{const d=t.target.textContent,o=e.data.documents.find(n=>n._id===d);e.set_state({selected_document:o})},"click .close-modal":(t={},e={})=>{e.set_state({selected_document:null})},"click .document-modal":(t={},e={})=>{t.target===t.currentTarget&&e.set_state({selected_document:null})}},render:({data:t,state:e,each:d,when:o})=>` -
-