No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    Recommendations on App architecture

    Help
    4
    20
    688
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • beets
      beets @SeRiusRod last edited by

      @SeRiusRod So to be clear, you’re planning on having a docker container that serves an SPA. This SPA is your scheduler interface. You’re not planning at this time to use either electron or cordova/capacitor. Correct so far?

      The confusion comes from the fact that neither mqtt nor curl can run in a user’s browser. You probably could replace curl with fetch / axios, but you’ll probably have cors issues. Thus, you’ll need a backend node process. The node process will run on the server, and the quasar app is running on the client’s device.

      Your structure would then be:

      On the API (server): run mqtt, your scheduler / cron logic, and make curl requests.

      On the quasar app (client): build an interface for scheduling, and POST or send via socket to api.

      You’re correct that you can link the quasar app and api via socket.io, or just use regular polling GET requests. Note that these two “processes” aren’t running on the same machine unless your docker container is running on your desktop as opposed to a raspberry pi or whatever.

      With this approach, your api is always running, and it will execute your scheduler no matter if a quasar page is open or not.

      And to answer your fundamental question, I would probably not try to build these two projects together somehow. You could make your API also serve the html / js / css of the build quasar project, but nginx is better at that and it reduces complexity.

      S 1 Reply Last reply Reply Quote 1
      • S
        SeRiusRod @beets last edited by

        Thanks Beets, yes, now that’s correct.

        @beets said in Recommendations on App architecture:

        Thus, you’ll need a backend node process.

        But before I go that approach, let me understand: If some code is added as a module or boot file, that code will not run in the server without the interface being active at the client?

        BTW, i don’t need an API. If i can setup an in memory model, like a vuex store, populate with mqtt info and periodically dump to a json file that would do.
        I have mqtt already running as a module.

        Wouldn’t another boot file work for the scheduler? (If it’s true that is code that runs in the server)

        beets 1 Reply Last reply Reply Quote 0
        • s.molinari
          s.molinari last edited by s.molinari

          @SeRiusRod - Quasar’s boot files aren’t for server-side code or rather don’t run server-side. Boot files get ran at the boot up time of your app and are used to initialize/ setup things like i18n, API connectivity, etc. So, you need an API, either via REST, GraphQL or even Electron to be able to accomplish server-side tasks.

          Scott

          1 Reply Last reply Reply Quote 0
          • beets
            beets @SeRiusRod last edited by

            @SeRiusRod said in Recommendations on App architecture:

            I have mqtt already running as a module.

            Can you show how this code is running? I may have been mistaken and mqtt can run in the browser via websockets. However to accomplish your main goal that the scheduler always runs (and to make sure that two schedulers aren’t running at the same time), you really should architect this app as a node process running on the server and a thin quasar client to display / edit data.

            S 1 Reply Last reply Reply Quote 0
            • S
              SeRiusRod @beets last edited by

              @beets said in Recommendations on App architecture:

              Can you show how this code is running?

              Of course:

              /* /src/boot/mqtt.js */
              import Vue from 'vue'
              const bus = new Vue()
              
              const mqtt = require('mqtt')
              
              export default ({ app }) => {
                window.mqtt = mqtt
                const client = mqtt.connect('mqtt://<myserver>')
                window.mqtt.bus = bus
                window.mqttClient = client
              
                client.on('connect', () => {
                  console.log('MQTT server connected successfully')
              
                  // # means subscribe to all data
                  client.subscribe('#', { qos: 0 }, (error) => {
                    if (!error) {
                      console.log('Subscription successful')
                    } else {
                      console.log('Subscription failed')
                    }
                  })
                })
              
                // Receive return data
                client.on('message', (topic, message) => {
                  console.log(topic, ': \t\t\t\t', message.toString())
                })
              
                // Disconnect and reconnect
                client.on('reconnect', (error) => {
                  console.log('Reconnecting...', error)
                })
                // Link exception handling
                client.on('error', (error) => {
                  console.log('Connection failed...', error)
                })
              
                setInterval(function () {
                  client.publish('tcc', 'Hello!')
                }, 5000)
              }
              
              export { mqtt }
              

              This is a lightened sample. Not that I have much more. I was still struggling to create a project wide storage to dump the values received.
              But you were completely right. There’s no code executed as long as there is no client connected.

              Now I’m building a nodejs service that receives mqtt info and stores in a json object that is committed to fs. Then it will schedule based on the events in that object, and also serve that json via sockets to the quasar frontend.

              Any suggestions on how to make that global json storage object?

              beets 1 Reply Last reply Reply Quote 0
              • beets
                beets @SeRiusRod last edited by

                @SeRiusRod said in Recommendations on App architecture:

                Any suggestions on how to make that global json storage object?

                Vuex is the typical answer here. It would be a pretty simple store, with likely just one variable called data or whatever. First question is, have you set anything up with vuex (the src/store folder in quasar) ?

                S 1 Reply Last reply Reply Quote 0
                • S
                  SeRiusRod @beets last edited by

                  @beets
                  That’s on the web side. But what about the server side?

                  beets 1 Reply Last reply Reply Quote 0
                  • beets
                    beets @SeRiusRod last edited by beets

                    @SeRiusRod Sorry I thought you had that figured out from here:

                    stores in a json object that is committed to fs.

                    If you’re just looking to store an object, a global variable works fine, unless I’m still misunderstanding.

                    S 2 Replies Last reply Reply Quote 0
                    • S
                      SeRiusRod last edited by SeRiusRod

                      I’ve tried with global variables, but I could not make it work.
                      Now I’ve made a separate module for the storage management:

                      // storage.js
                      const fs = require('fs');
                      
                      let settings = null;
                      let devices = null;
                      
                      function getSettings() {
                        try {
                          settings = JSON.parse(fs.readFileSync('./settings.json', 'utf8'));
                        } catch (error) {
                          console.log('settings.json file not found.');
                        }
                        if (!settings)
                          settings = {
                            A: true,
                            B: false,
                          };
                        console.log(settings);
                        return settings;
                      }
                      
                      function setSettings(newSettings) {
                        fs.writeFileSync('./settings.json', JSON.stringify(newSettings));
                      }
                      
                      function getDevices() {
                        try {
                          devices = JSON.parse(fs.readFileSync('./devices.json', 'utf8'));
                        } catch (error) {
                          console.log('devices.json file not found.');
                        }
                        if (!devices) devices = {};
                        console.log(devices);
                        return devices;
                      }
                      
                      function setDevices(newDevices) {
                        fs.writeFileSync('./devices.json', JSON.stringify(newDevices));
                      }
                      
                      module.exports = {
                        settings,
                        devices,
                        getSettings,
                        setSettings,
                        getDevices,
                        setDevices,
                      };
                      
                      // server.js
                      const config = require('./config');
                      
                      var storage = require('./storage.js');
                      const mqtt = require('./mqtt.js')
                      
                      function main() {
                        var settings = storage.getSettings();
                        console.log(`settings ${settings}`);   //Works
                        console.log(`storage.settings ${storage.settings}`); //Doesn't
                      
                        setInterval(function() {
                          console.log('*Timer tick!*');
                        }, 5000);
                      }
                      
                      main();
                      

                      If you look on the two console outputs, the second one is null. And I don’t see why. So now I want to populate storage on mqtt.js, but isn’t available there.

                      1 Reply Last reply Reply Quote 0
                      • S
                        SeRiusRod @beets last edited by

                        @beets said in Recommendations on App architecture:

                        If you’re just looking to store an object, a global variable works fine, unless I’m still misunderstanding.

                        Yes, you’re right. I was having a background error that prevented my project to work as intended and that confused me.

                        1 Reply Last reply Reply Quote 0
                        • S
                          SeRiusRod @beets last edited by

                          I’ve made a bare NodeJs backend and setup a global variable for devices storage. That added a socket.io file that periodically sends a device.
                          On the Quasar frontend, I’ve added a boot file and setup one page for receiving the data. But I can’t find why it doesn’t receive anything, and the backend doesn’t inform about client’s connection. Perhaps you can give me a hand:

                          Backend:

                          // socket.js
                          const config = require('./config');
                          
                          const app = require('express')();
                          const http = require('http').Server(app);
                          const io = require('socket.io')(http);
                          
                          console.log('socket: module loaded');   //This is shown
                          
                          app.get('/', (req, res) => {
                            res.status(200).send('This is the backend server.');    //This works
                          });
                          
                          function sendDevice(id) {
                            const device = devices.find((i) => i.id === id);
                            io.emit('device', device);
                          }
                          
                          function updateDevice(id, data) {
                            var device = devices.find((i) => i.id === id);
                            if (!device) devices.push(data);
                            else device = data;
                            pendingCommit = true;
                          }
                          
                          http.listen(config.socketPort, () => {
                            console.log('socket: websocket listening on *:' + config.socketPort);   //This works
                          });
                          
                          // All of this doesn't seem to work
                          io.on('connection', (socket) => {
                            console.log(`socket: client connected with id: ${socket.id}`);
                          
                            socket.on('updateDevice', function(data) {
                              if (!data.id) {
                                console.error('socket: [ERROR] Received a device update request without a correct id.');
                                return;
                              }
                              updateDevice(data.id, data);
                            });
                          
                            setInterval(function() {
                              sendDevice('1');
                            }, 5000);
                          
                            io.on('disconnect', () => {
                              console.log(`socket: client disconnected with id: ${socket.id}`);
                            });
                          });
                          
                          module.exports = {
                            sendDevice
                          };
                          

                          Frontend:

                          // boot/socket.js
                          import io from 'socket.io-client'
                          
                          const socket = io.connect('http://localhost:3002')
                          
                          export default ({ Vue }) => {
                            Vue.prototype.$socket = socket
                          }
                          export { socket }
                          
                          <!-- pages/info.vue -->
                          <template>
                            <q-page padding class="window-height window-width row justify-center">
                              Selected: {{selected}}<br/>  <!-- Shows the correct Id -->
                              Name: {{device.display_name}} <!-- Shows default -->
                              <q-knob
                                readonly
                                v-model="device.battery_level"
                                show-value
                                size="150px"
                                :thickness="0.22"
                                color="green"
                                track-color="green-2"
                                class="q-ma-md"
                              >
                                {{ device.battery_level }}%
                              </q-knob>
                            </q-page>
                          </template>
                          
                          <script>
                          export default {
                            name: 'Info',
                            data () {
                              return {
                                selected: '',
                                settings: [],
                                device: {
                                  id: '',
                                  display_name: 'Empty',
                                  battery_level: '-1'
                                }
                              }
                            },
                            mounted () {
                              this.selected = this.$q.localStorage.getItem('selectedItem')
                          
                              this.$socket.on('device', (data) => {
                                this.device = [...this.device, data]
                              })
                            }
                          }
                          </script>
                          
                          1 Reply Last reply Reply Quote 0
                          • S
                            SeRiusRod last edited by

                            Okey. I was getting a silent Cors error.

                            1 Reply Last reply Reply Quote 0
                            • First post
                              Last post