Solved
brianh

Sep 24th, 2023 04:37 AM

Hi Guys,

Pulling my hair out here. So I though I would share and ask for help.

I am currently building a Database Browser application that would allow a user to browse a database and read data from their respective tables all within the browser using my app.

You can see more at https://dbctrl.com (Be nice, it's a work in progress).

Now, I have been able to build the application in such a way that the user can open a local database and view the contents of the server. That's not what I am going for because their database is not on my server. Given the constraints of opening a local network database through the browser, I want to use WebAssembly for this to avoid any complications. I do not know WebAssembly at all.

So to show you what I have so far, here is the view for a user to add a database connection:

@extends('theme::layouts.main')

@section('content')

<div x-data="{ modalOpen: false, connectionType: 'remote' }" class="flex flex-col w-full p-4">

    <div x-show="modalOpen" class="fixed top-0 left-0 z-[99] flex items-center justify-center w-screen h-screen"
        x-cloak>
        <div x-show="modalOpen" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
            x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-300"
            x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" @click="modalOpen=false"
            class="absolute inset-0 w-full h-full bg-black bg-opacity-40"></div>
        <div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-300"
            x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
            x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200"
            x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
            x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
            class="relative w-full py-6 bg-white px-7 sm:max-w-lg sm:rounded-lg">
            <div class="flex items-center justify-between pb-2">
                <h3 class="text-lg font-semibold">Add MySQL Connection</h3>
                <button @click="modalOpen=false"
                    class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
                    <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                        stroke-width="1.5" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
                </button>
            </div>

            <!-- Form for connection details -->
            <form>
                <!-- ... -->
                <label for="connection_type">Connection Type</label>
                <select id="connection_type" name="connection_type" @change="updateConnectionType($event)">
                    <option value="remote" selected>Remote</option>
                    <option value="local">Local</option>
                </select>

                <label
                    class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
                    for="name">Name</label>
                <input type="text" id="name" name="name" placeholder="E.g. My App DB"
                    class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
                    required>
                <label
                    class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
                    for="hostname">Host</label>
                <input type="text" id="hostname" name="hostname" placeholder="E.g. 10.10.10.10"
                    class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
                    required>
                <label
                    class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
                    for="username">Username</label>
                <input type="text" id="username" name="username" placeholder="Username"
                    class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50">
                <label
                    class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
                    for="password">Password</label>
                <input type="password" id="password" name="password" placeholder="Password"
                    class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50">
                <label
                    class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
                    for="database_name">Database Name</label>
                <input type="text" id="database_name" name="database_name" placeholder="E.g. MainDB"
                    class="mb-2 flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"
                    required>
                <button type="submit" onclick="submitForm()"
                    class="mt-2 inline-flex items-center justify-center h-10 w-28 px-4 py-2 text-sm font-medium tracking-wide text-black shadow-lg transition-colors duration-100 bg-white border-2 border-yellow-500 rounded-md hover:text-black hover:bg-yellow-200">Save</button>
            </form>
        </div>
    </div>
    <button @click="modalOpen=true"
        class="mt-2 mb-4 inline-flex items-center justify-center h-10 w-28 px-4 py-2 text-sm font-medium tracking-wide text-black shadow-lg transition-colors duration-100 bg-white border-2 border-yellow-500 rounded-md hover:text-black hover:bg-yellow-200">Add
    </button>


    @if($databaseConnections->isEmpty())
    <div class="flex flex-col items-center justify-center mt-10">
        <img src="https://cdn.devdojo.com/tails/images/5aw7TwTWRPf7OixApMpGvN5rYsajsI1xQ7AK1WlC.png"
            alt="No connections image" class="mb-6 h-60">
        <h2 class="text-xl font-bold text-gray-700">No Database Connections</h2>
        <p class="text-gray-600">You currently have no database connections. Click "Add" to create one.</p>
    </div>
    @else
    @foreach($databaseConnections as $databaseConnection)
    <a href="{{ route('database.show', $databaseConnection->name) }}"
        class="max-w-sm border-yellow-500 border-2 bg-white rounded-lg shadow-lg p-7 mb-4 hover:text-yellow-200 hover:bg-yellow-200 hover:border-yellow-500">
        <div class="flex justify-between items-center mb-2">
            <h5 class="text-xl font-bold leading-none tracking-tight text-black">
                {{ $databaseConnection->name }}
            </h5>
            <div>
                <span
                    class="rounded-full px-2.5 py-0.5 text-black text-xs font-semibold border-yellow-500 border-2 bg-white">
                    MySQL
                </span>
            </div>
            <div @click="deleteConnection({{ $databaseConnection->id }})" class="cursor-pointer">
                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="red"
                    class="w-6 h-6">
                    <path stroke-linecap="round" stroke-linejoin="round"
                        d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5m6 4.125l2.25 2.25m0 0l2.25 2.25M12 13.875l2.25-2.25M12 13.875l-2.25 2.25M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
                </svg>
            </div>
        </div>

    </a>
    @endforeach
    @endif

    <script>
        function submitForm() {
            const formData = new FormData(document.querySelector('form'));
            const connectionType = formData.get('connection_type');

            if (connectionType === 'local') {
                // Use Wasm library to connect to local MySQL
                // This is pseudocode and would depend on the library's API:
                const wasmMySQL = new WasmMySQLLibrary();
                wasmMySQL.connect(
                    formData.get('hostname'),
                    formData.get('username'),
                    formData.get('password'),
                    formData.get('database_name')
                );
                // NOTE: You might need to handle the response from wasmMySQL.connect
                // and update your UI or perform other tasks.
            } else {
                // Existing logic for remote connections
                fetch('/dashboard/add-connection', {
                        method: 'POST',
                        body: formData,
                        headers: {
                            'X-Requested-With': 'XMLHttpRequest',
                            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                        }
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.message) {
                            alert(data.message);
                            location.reload(); // Reload the page
                        }
                    })
                    .catch(error => {
                        console.error('Error:', error);
                    });
            }
        }


        function deleteConnection(id) {
            if (!confirm("Are you sure you want to delete this connection?")) {
                return;
            }

            fetch(`/dashboard/connection/${id}`, {
                    method: 'DELETE',
                    headers: {
                        'X-Requested-With': 'XMLHttpRequest',
                        'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                    }
                })
                .then(response => response.json())
                .then(data => {
                    if (data.message) {
                        alert(data.message);
                        location.reload(); // Reload the page
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }

        window.updateConnectionType = function(event) {
            this.connectionType = event.target.value;
            // You can hide/show or enable/disable certain fields based on this value if needed
        }

    </script>

    @endsection

Does anyone have any clue as to how I can implement this with a WASM MySQL driver or whatever so that the user can open their own local database with my tool and browse the data in the tables?

bobbyiliev

Sep 24th, 2023 05:12 AM

Best Answer

Hi there,

I've not done this before but I believe that even with WebAssembly, browser-based apps you would not have direct access to OS-level TCP sockets. This means you cannot directly connect to a MySQL instance using the typical TCP/IP protocol from the browser, even with Wasm. You'd need some local bridge/proxy to convert Web API calls (like WebSockets or HTTP) into MySQL protocol calls but this would be a bit of a limitation because your clients will need to set that up.

Maybe a better approach would be to build a native desktop app, now that NativePHP is out:

NativePHP is a new way to build native applications, using the tools you already know.

Another possible approach might be to build a browser extension rather than a desktop app or a web app, here is a video tutorial that shows how you might be able to do that:

And this is the article that comes with the video:

https://anobjectisa.com/?p=105