diff --git a/.env b/.env new file mode 100644 index 0000000..54220b3 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +COMMONERS_AUTH_STRING = 'adsfsafasdfasdfasdfsafsadfdsafasdfsda' +PORT = 8080 \ No newline at end of file diff --git a/commoners.config.ts b/commoners.config.ts index 56e2dc4..df3c0d4 100644 --- a/commoners.config.ts +++ b/commoners.config.ts @@ -1,18 +1,21 @@ -// ------------- PRODUCTION ------------- -import * as bluetoothPlugin from '@commoners/bluetooth' -import * as serialPlugin from '@commoners/serial' -// import { defineConfig } from 'commoners' // NOTE: COMMONERS dependencies are missing in local development... +// // ------------- PRODUCTION ------------- +// import * as bluetoothPlugin from '@commoners/bluetooth' +// import * as serialPlugin from '@commoners/serial' +// import localServicesPlugin from '@commoners/local-services' +// // import { defineConfig } from 'commoners' // NOTE: COMMONERS dependencies are missing in local development... -// // ------------- DEVELOPMENT ------------- -// import * as bluetoothPlugin from '../commoners/packages/plugins/devices/ble/index.js' -// import * as serialPlugin from '../commoners/packages/plugins/devices/serial/index.js' -// // import { defineConfig } from '../commoners/packages/core/index' // NOTE: COMMONERS dependencies are missing in local development... +// ------------- DEVELOPMENT ------------- +import * as bluetoothPlugin from '../commoners/packages/plugins/devices/ble/index.js' +import * as serialPlugin from '../commoners/packages/plugins/devices/serial/index.js' +import localServicesPlugin from '../commoners/packages/plugins/local-services/index.js' +// import { defineConfig } from '../commoners/packages/core/index' // NOTE: COMMONERS dependencies are missing in local development... -// // ----------- Package.json Dependencies ----------- -// // "@commoners/autoupdate": "file:../commoners/packages/plugins/autoupdate", -// // "@commoners/bluetooth": "file:../commoners/packages/plugins/devices/ble", -// // "@commoners/serial": "file:../commoners/packages/plugins/devices/serial", -// // "commoners": "file:../commoners" +// ----------- Package.json Dependencies ----------- +// "@commoners/autoupdate": "file:../commoners/packages/plugins/autoupdate", +// "@commoners/bluetooth": "file:../commoners/packages/plugins/devices/ble", +// "@commoners/serial": "file:../commoners/packages/plugins/devices/serial", +// "@commoners/local-services": "file:../commoners/packages/plugins/local-services", +// "commoners": "file:../commoners" const defineConfig = (o) => o @@ -26,6 +29,11 @@ export default defineConfig({ // autoUpdatePlugin, bluetoothPlugin, serialPlugin, + localServicesPlugin(null, (ip, env) => { + const isLocalIP = process.env.LOCAL_IP === ip + const hasAuthString = process.env.COMMONERS_AUTH_STRING === env.COMMONERS_AUTH_STRING + return hasAuthString || isLocalIP + }), { name: 'selective-builds', @@ -56,16 +64,18 @@ export default defineConfig({ services: { - // Example Node server (using pkg) + // Packaged with pkg node: { + description: 'A simple Node.js server', src: './src/services/node/index.js', publish: 'https://node-production-aa81.up.railway.app/' }, - // Example Python server (using pyinstaller) + // Packaged with pyinstaller python: { + description: 'A simple Python server', src: './src/services/python/main.py', - port: 3768, + port: 1234, publish: { build: 'python -m PyInstaller --name solidarity --onedir --clean ./src/services/python/main.py --distpath ./dist/services/python', remote: 'https://python-production-4f11.up.railway.app', @@ -74,7 +84,7 @@ export default defineConfig({ }, remote: 'https://jsonplaceholder.typicode.com', dynamic: { - src: 'http://localhost:3768', // Call the python server in development + src: 'http://localhost:1234', // Call the python server in development publish: 'https://jsonplaceholder.typicode.com' } } diff --git a/index.html b/index.html index a0cfccb..8a338e5 100644 --- a/index.html +++ b/index.html @@ -23,14 +23,16 @@

Solidarity ✊

- -
    +
    diff --git a/package-lock.json b/package-lock.json index 6713bec..a546262 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,9 @@ "@capacitor/core": "^5.3.0", "@capacitor/ios": "^5.3.0", "@commoners/bluetooth": "0.0.0", + "@commoners/local-services": "0.0.0", "@commoners/serial": "0.0.0", - "commoners": "0.0.15" + "commoners": "0.0.16" } }, "node_modules/@ampproject/remapping": { @@ -2023,6 +2024,12 @@ "integrity": "sha512-42szBrZBMxxXM6nW1D7dZ6xg42YjrkiKWvrBJ9bafQ71JPHvpjVM5i55pUQ98tQV76h2QmdtRZME5S7zF1LQsA==", "dev": true }, + "node_modules/@commoners/local-services": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@commoners/local-services/-/local-services-0.0.0.tgz", + "integrity": "sha512-y5Cadb8T/vLnFvWTCRBZW4K+GxOokTByE2Lg0vcgcb81vfCy9XWYYO1G6ZLPPcDSjnEngLZAq8aIxRfLxfcdgA==", + "dev": true + }, "node_modules/@commoners/node-demo": { "resolved": "src/services/node", "link": true @@ -2174,9 +2181,9 @@ } }, "node_modules/@electron/remote": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.0.11.tgz", - "integrity": "sha512-PYEs7W3GrQNuhgiMHjFEvL5MbAL6C7m1AwSAHGqC+xc33IdP7rcGtJSdTP2eg1ssyB3oI00KwTsiSlsQbAoXpA==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.0.12.tgz", + "integrity": "sha512-IJN6xLAxptq5MCvXNCU6+pdQyz0DjpPtX6g2TPJftu3Z9pU6BTdnos9ZMN8nK471LkASqiA6C+Hzjv5SS8PAQw==", "dev": true, "peerDependencies": { "electron": ">= 13.0.0" @@ -3328,10 +3335,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz", - "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==", - "dev": true + "version": "20.8.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz", + "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==", + "dev": true, + "dependencies": { + "undici-types": "~5.25.1" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.2", @@ -4614,9 +4624,9 @@ } }, "node_modules/commoners": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/commoners/-/commoners-0.0.15.tgz", - "integrity": "sha512-Ki5pP7SLMzhZ2X/TfoKNHPA4fkw4tfMpyBIwzpugKfFpvgLYCoQhbVqIHKw+M+lypJreu385MZ9mk0AaItUcVQ==", + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/commoners/-/commoners-0.0.16.tgz", + "integrity": "sha512-VIK9TABtQHg6JPcpKtG94KAwaIa0k0yRKdreHJbo7R0PDOgprwZhfMjWQ1GbZxhkzqfqYFjd4fhtGVO4+RcLlQ==", "dev": true, "dependencies": { "@electron-toolkit/tsconfig": "^1.0.1", @@ -4627,6 +4637,7 @@ "chalk": "^5.2.0", "clui": "^0.3.6", "configstore": "^6.0.0", + "dotenv": "^16.3.1", "electron": "^26.2.1", "electron-builder": "^23.6.0", "esbuild": "^0.17.19", @@ -5578,12 +5589,15 @@ } }, "node_modules/dotenv": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", - "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, "node_modules/dotenv-expand": { @@ -5830,9 +5844,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.544", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz", - "integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w==", + "version": "1.4.545", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.545.tgz", + "integrity": "sha512-G1HKumUw+y5yxMjewGfKz0XrqG6O+Tb4zrlC/Vs1+9riRXBuFlO0hOEXP3xeI+ltlJkbVUuLkYdmjHYH6Jkiow==", "dev": true }, "node_modules/electron/node_modules/@types/node": { @@ -10158,6 +10172,15 @@ "node": ">=12.0.0" } }, + "node_modules/read-config-file/node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -12176,6 +12199,12 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "dev": true + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 0cf2735..3484ed2 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "build:frontend": "commoners build --frontend", "build:services": "commoners build --services", "build:node": "cd src/services/node && npm run build", + "share": "commoners share", "launch": "commoners launch", "launch:desktop": "commoners launch --desktop", "launch:mobile": "commoners launch --mobile", @@ -34,6 +35,7 @@ "@capacitor/ios": "^5.3.0", "@commoners/bluetooth": "0.0.0", "@commoners/serial": "0.0.0", - "commoners": "0.0.15" + "@commoners/local-services": "0.0.0", + "commoners": "0.0.16" } } diff --git a/src/frontend/main.ts b/src/frontend/main.ts index 7cbd996..f11345b 100644 --- a/src/frontend/main.ts +++ b/src/frontend/main.ts @@ -1,17 +1,25 @@ import { BleClient } from '@capacitor-community/bluetooth-le'; -const list = document.querySelector('ul') -async function createClient(url: string | URL) { +const list = document.querySelector('#commands') as HTMLUListElement +async function createClient(url: string | URL, id = '') { + + url = new URL('.commoners', url) + const client = await new SwaggerClient(typeof url === 'string' ? url : url.href).catch((e: any) => { throw new Error(`Failed to create client for ${url}: ${e.message}`) }) + const nestedList = document.createElement('ul') + nestedList.id = id + list.appendChild(nestedList) + + const { title, description } = client.spec.info const section = document.createElement('section') - section.innerHTML = `

    ${title}

    ${description}` - list.appendChild(section) + section.innerHTML = `

    ${title}

    ${description}` + nestedList.append(section) // Populate list of available methods Object.keys(client.spec.paths).forEach((path: any) => { @@ -32,7 +40,7 @@ async function createClient(url: string | URL) { } li.append(container, button) - list.appendChild(li) + nestedList.appendChild(li) }) }) @@ -104,7 +112,7 @@ if (COMMONERS.services.python) { const pythonUrl = new URL(COMMONERS.services.python.url) // Equivalent to commoners://python setTimeout(async () => { - const client = await createClient(new URL('.commoners', pythonUrl)) + const client = await createClient(pythonUrl) client.apis.version.getPythonVersion().then(res => { onData({ source: 'Python', command: 'version', payload: res.body }) }); @@ -129,10 +137,33 @@ async function requestSerialPort () { } -COMMONERS.ready.then(() => { +COMMONERS.ready.then(plugins => { + + if ('local-services' in plugins) { + + const localServices = plugins['local-services'] + const ids: { [x:string]: string } = {} + + localServices.onFound((url) => { + if (ids[url]) return + const id = ids[url] = Math.random().toString(36).substring(7) + createClient(url, id) + }) + + localServices.onClosed((url) => { + const el = document.getElementById(ids[url]) + if (el) el.remove() + delete ids[url] + }) + + localServices.get() + + } + + const testSerialConnection = document.getElementById('testSerialConnection') if (testSerialConnection) { - if ('serial' in COMMONERS.plugins.loaded) testSerialConnection.addEventListener('click', requestSerialPort) + if ('serial' in plugins) testSerialConnection.addEventListener('click', requestSerialPort) else testSerialConnection.setAttribute('disabled', '') } // --------- Web Bluetooth Test --------- @@ -150,7 +181,7 @@ COMMONERS.ready.then(() => { const testBluetoothConnection = document.getElementById('testBluetoothConnection') if (testBluetoothConnection) { - if ('bluetooth' in COMMONERS.plugins.loaded) testBluetoothConnection.addEventListener('click', requestBluetoothDevice) + if ('bluetooth' in plugins) testBluetoothConnection.addEventListener('click', requestBluetoothDevice) else testBluetoothConnection.setAttribute('disabled', '') } diff --git a/src/frontend/styles.css b/src/frontend/styles.css index bd1955d..87d2e95 100644 --- a/src/frontend/styles.css +++ b/src/frontend/styles.css @@ -27,6 +27,11 @@ main { justify-content: center; gap: 50px; flex-wrap: wrap; + flex-direction: row-reverse; +} + +#sidebar { + flex-grow: 1 } #console { @@ -120,7 +125,7 @@ ul { overflow-y: auto; } -#commands h2 { +#commands h3 { margin-bottom: 0; } diff --git a/src/services/python/openapi.json b/src/services/python/openapi.json index 989327c..1816acb 100644 --- a/src/services/python/openapi.json +++ b/src/services/python/openapi.json @@ -2,73 +2,79 @@ "openapi": "3.0.3", "info": { "title": "Python Server", - "description": "A simple Python server", - "version": "1.0.0", - "contact": { - "name": "Garrett Flynn", - "email": "garrettmflynn@gmail.com", - "url": "garrettflynn.com" - } - }, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - { - "name": "version", - "description": "Version endpoint" - }, - { - "name": "users", - "description": "User endpoints" - }, - { - "name": "echo", - "description": "Echo endpoints" - } - ], - "paths": { - "/version": { - "get": { - "tags": ["version"], - "operationId": "getPythonVersion", - "description": "Get the active version of Python", - "parameters": [], - "responses": { - "200": { - "description": "OK" - } + "description": "A simple Python server", + "version": "1.0.0", + "contact": { + "name": "Garrett Flynn", + "email": "garrettmflynn@gmail.com", + "url": "garrettflynn.com" } - } }, - "/users": { - "get": { - "tags": ["users"], - "operationId": "getUsers", - "description": "Get an array of users", - "parameters": [], - "responses": { - "200": { - "description": "OK" - } - } + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + { + "name": "version", + "description": "Version endpoint" + }, + { + "name": "users", + "description": "User endpoints" + }, + { + "name": "echo", + "description": "Echo endpoints" } - }, - "/echo": { - "post": { - "tags": ["echo"], - "operationId": "echo", - "description": "Return the request body back to the user", - "parameters": [], - "responses": { - "200": { - "description": "OK" + ], + "paths": { + "/version": { + "get": { + "tags": [ + "version" + ], + "operationId": "getPythonVersion", + "description": "Get the active version of Python", + "parameters": [], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/users": { + "get": { + "tags": [ + "users" + ], + "operationId": "getUsers", + "description": "Get an array of users", + "parameters": [], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/echo": { + "post": { + "tags": [ + "echo" + ], + "operationId": "echo", + "description": "Return the request body back to the user", + "parameters": [], + "responses": { + "200": { + "description": "OK" + } + } } - } } - } -} + } } \ No newline at end of file