diff --git a/server/.gitignore b/server/.gitignore
new file mode 100644
index 0000000..82cf9aa
--- /dev/null
+++ b/server/.gitignore
@@ -0,0 +1,121 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+.env.production
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+.sqlite
+
+lib/
diff --git a/server/.npmignore b/server/.npmignore
new file mode 100644
index 0000000..82cf9aa
--- /dev/null
+++ b/server/.npmignore
@@ -0,0 +1,121 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+.env.production
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+.sqlite
+
+lib/
diff --git a/server/.prettierrc b/server/.prettierrc
new file mode 100644
index 0000000..2df6504
--- /dev/null
+++ b/server/.prettierrc
@@ -0,0 +1,9 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "printWidth": 110,
+ "semi": false,
+ "trailingComma": "none",
+ "singleQuote": true,
+ "parser": "typescript"
+}
\ No newline at end of file
diff --git a/server/config/custom-environment-variables.json b/server/config/custom-environment-variables.json
new file mode 100644
index 0000000..283387f
--- /dev/null
+++ b/server/config/custom-environment-variables.json
@@ -0,0 +1,10 @@
+{
+ "port": {
+ "__name": "PORT",
+ "__format": "number"
+ },
+ "host": "HOSTNAME",
+ "authentication": {
+ "secret": "FEATHERS_SECRET"
+ }
+}
\ No newline at end of file
diff --git a/server/config/default.json b/server/config/default.json
new file mode 100644
index 0000000..2da2ca8
--- /dev/null
+++ b/server/config/default.json
@@ -0,0 +1,17 @@
+{
+ "host": "localhost",
+ "port": 3030,
+ "public": "./public/",
+ "origins": [
+ "http://localhost:3030"
+ ],
+ "paginate": {
+ "default": 10,
+ "max": 50
+ },
+ "sqlite": {
+ "client": "sqlite3",
+ "connection": "server.sqlite",
+ "useNullAsDefault": true
+ }
+}
\ No newline at end of file
diff --git a/server/config/test.json b/server/config/test.json
new file mode 100644
index 0000000..ab43666
--- /dev/null
+++ b/server/config/test.json
@@ -0,0 +1,3 @@
+{
+ "port": 8998
+}
\ No newline at end of file
diff --git a/server/knexfile.ts b/server/knexfile.ts
new file mode 100644
index 0000000..9b789de
--- /dev/null
+++ b/server/knexfile.ts
@@ -0,0 +1,7 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html
+import { app } from './src/app'
+
+// Load our database connection info from the app configuration
+const config = app.get('sqlite')
+
+module.exports = config
diff --git a/server/node_modules/.yarn-integrity b/server/node_modules/.yarn-integrity
new file mode 100644
index 0000000..ae93109
--- /dev/null
+++ b/server/node_modules/.yarn-integrity
@@ -0,0 +1,10 @@
+{
+ "systemParams": "linux-x64-108",
+ "modulesFolders": [],
+ "flags": [],
+ "linkedModules": [],
+ "topLevelPatterns": [],
+ "lockfileEntries": {},
+ "files": [],
+ "artifacts": {}
+}
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..157d59f
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "server",
+ "description": "The server for handling two way communication with a iot Device",
+ "version": "0.0.0",
+ "homepage": "",
+ "private": true,
+ "keywords": [
+ "feathers"
+ ],
+ "author": {},
+ "contributors": [],
+ "bugs": {},
+ "engines": {
+ "node": ">= 18.16.0"
+ },
+ "feathers": {
+ "language": "ts",
+ "packager": "yarn",
+ "database": "sqlite",
+ "framework": "koa",
+ "transports": [
+ "rest",
+ "websockets"
+ ],
+ "schema": "typebox"
+ },
+ "directories": {
+ "lib": "src",
+ "test": "test"
+ },
+ "files": [
+ "lib/client.js",
+ "lib/**/*.d.ts",
+ "lib/**/*.shared.js"
+ ],
+ "main": "lib/client",
+ "scripts": {
+ "dev": "nodemon -x ts-node src/index.ts",
+ "compile": "shx rm -rf lib/ && tsc",
+ "start": "node lib/",
+ "prettier": "npx prettier \"**/*.ts\" --write",
+ "mocha": "cross-env NODE_ENV=test mocha test/ --require ts-node/register --recursive --extension .ts --exit",
+ "test": "cross-env NODE_ENV=test npm run migrate && npm run mocha",
+ "bundle:client": "npm run compile && npm pack --pack-destination ./public",
+ "migrate": "knex migrate:latest",
+ "migrate:make": "knex migrate:make"
+ }
+}
\ No newline at end of file
diff --git a/server/public/index.html b/server/public/index.html
new file mode 100644
index 0000000..5a57b63
--- /dev/null
+++ b/server/public/index.html
@@ -0,0 +1,37 @@
+
+
+
+ server
+
+
+
+
+
+
+
+
+
diff --git a/server/readme.md b/server/readme.md
new file mode 100644
index 0000000..0ba3ed6
--- /dev/null
+++ b/server/readme.md
@@ -0,0 +1,42 @@
+# server
+
+> The server for handling two way communication with a iot Device
+
+## About
+
+This project uses [Feathers](http://feathersjs.com). An open source framework for building APIs and real-time applications.
+
+## Getting Started
+
+1. Make sure you have [NodeJS](https://nodejs.org/) and [npm](https://www.npmjs.com/) installed.
+2. Install your dependencies
+
+ ```
+ cd path/to/server
+ npm install
+ ```
+
+3. Start your app
+
+ ```
+ npm run compile # Compile TypeScript source
+ npm run migrate # Run migrations to set up the database
+ npm start
+ ```
+
+## Testing
+
+Run `npm test` and all your tests in the `test/` directory will be run.
+
+## Scaffolding
+
+This app comes with a powerful command line interface for Feathers. Here are a few things it can do:
+
+```
+$ npx feathers help # Show all commands
+$ npx feathers generate service # Generate a new Service
+```
+
+## Help
+
+For more information on all the things you can do with Feathers visit [docs.feathersjs.com](http://docs.feathersjs.com).
diff --git a/server/src/app.ts b/server/src/app.ts
new file mode 100644
index 0000000..cf8129b
--- /dev/null
+++ b/server/src/app.ts
@@ -0,0 +1,54 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/application.html
+import { feathers } from '@feathersjs/feathers'
+import configuration from '@feathersjs/configuration'
+import { koa, rest, bodyParser, errorHandler, parseAuthentication, cors, serveStatic } from '@feathersjs/koa'
+import socketio from '@feathersjs/socketio'
+
+import { configurationValidator } from './configuration'
+import type { Application } from './declarations'
+import { logError } from './hooks/log-error'
+import { sqlite } from './sqlite'
+import { services } from './services/index'
+import { channels } from './channels'
+
+const app: Application = koa(feathers())
+
+// Load our app configuration (see config/ folder)
+app.configure(configuration(configurationValidator))
+
+// Set up Koa middleware
+app.use(cors())
+app.use(serveStatic(app.get('public')))
+app.use(errorHandler())
+app.use(parseAuthentication())
+app.use(bodyParser())
+
+// Configure services and transports
+app.configure(rest())
+app.configure(
+ socketio({
+ cors: {
+ origin: app.get('origins')
+ }
+ })
+)
+app.configure(channels)
+app.configure(sqlite)
+app.configure(services)
+
+// Register hooks that run on all service methods
+app.hooks({
+ around: {
+ all: [logError]
+ },
+ before: {},
+ after: {},
+ error: {}
+})
+// Register application setup and teardown hooks here
+app.hooks({
+ setup: [],
+ teardown: []
+})
+
+export { app }
diff --git a/server/src/channels.ts b/server/src/channels.ts
new file mode 100644
index 0000000..9c72f46
--- /dev/null
+++ b/server/src/channels.ts
@@ -0,0 +1,38 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/channels.html
+import type { RealTimeConnection, Params } from '@feathersjs/feathers'
+import type { AuthenticationResult } from '@feathersjs/authentication'
+import '@feathersjs/transport-commons'
+import type { Application, HookContext } from './declarations'
+import { logger } from './logger'
+
+export const channels = (app: Application) => {
+ logger.warn(
+ 'Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.'
+ )
+
+ app.on('connection', (connection: RealTimeConnection) => {
+ // On a new real-time connection, add it to the anonymous channel
+ app.channel('anonymous').join(connection)
+ })
+
+ app.on('login', (authResult: AuthenticationResult, { connection }: Params) => {
+ // connection can be undefined if there is no
+ // real-time connection, e.g. when logging in via REST
+ if (connection) {
+ // The connection is no longer anonymous, remove it
+ app.channel('anonymous').leave(connection)
+
+ // Add it to the authenticated user channel
+ app.channel('authenticated').join(connection)
+ }
+ })
+
+ // eslint-disable-next-line no-unused-vars
+ app.publish((data: any, context: HookContext) => {
+ // Here you can add event publishers to channels set up in `channels.js`
+ // To publish only for a specific event use `app.publish(eventname, () => {})`
+
+ // e.g. to publish all service events to all authenticated users use
+ return app.channel('authenticated')
+ })
+}
diff --git a/server/src/client.ts b/server/src/client.ts
new file mode 100644
index 0000000..ad1286d
--- /dev/null
+++ b/server/src/client.ts
@@ -0,0 +1,34 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/client.html
+import { feathers } from '@feathersjs/feathers'
+import type { TransportConnection, Application } from '@feathersjs/feathers'
+import authenticationClient from '@feathersjs/authentication-client'
+import type { AuthenticationClientOptions } from '@feathersjs/authentication-client'
+
+export interface Configuration {
+ connection: TransportConnection
+}
+
+export interface ServiceTypes {}
+
+export type ClientApplication = Application
+
+/**
+ * Returns a typed client for the server app.
+ *
+ * @param connection The REST or Socket.io Feathers client connection
+ * @param authenticationOptions Additional settings for the authentication client
+ * @see https://dove.feathersjs.com/api/client.html
+ * @returns The Feathers client application
+ */
+export const createClient = (
+ connection: TransportConnection,
+ authenticationOptions: Partial = {}
+) => {
+ const client: ClientApplication = feathers()
+
+ client.configure(connection)
+ client.configure(authenticationClient(authenticationOptions))
+ client.set('connection', connection)
+
+ return client
+}
diff --git a/server/src/configuration.ts b/server/src/configuration.ts
new file mode 100644
index 0000000..44eaa0c
--- /dev/null
+++ b/server/src/configuration.ts
@@ -0,0 +1,17 @@
+import { Type, getValidator, defaultAppConfiguration } from '@feathersjs/typebox'
+import type { Static } from '@feathersjs/typebox'
+
+import { dataValidator } from './validators'
+
+export const configurationSchema = Type.Intersect([
+ defaultAppConfiguration,
+ Type.Object({
+ host: Type.String(),
+ port: Type.Number(),
+ public: Type.String()
+ })
+])
+
+export type ApplicationConfiguration = Static
+
+export const configurationValidator = getValidator(configurationSchema, dataValidator)
diff --git a/server/src/declarations.ts b/server/src/declarations.ts
new file mode 100644
index 0000000..b35505e
--- /dev/null
+++ b/server/src/declarations.ts
@@ -0,0 +1,20 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html
+import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers'
+import { Application as FeathersApplication } from '@feathersjs/koa'
+import { ApplicationConfiguration } from './configuration'
+
+export { NextFunction }
+
+// The types for app.get(name) and app.set(name)
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface Configuration extends ApplicationConfiguration {}
+
+// A mapping of service names to types. Will be extended in service files.
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface ServiceTypes {}
+
+// The application instance type that will be used everywhere else
+export type Application = FeathersApplication
+
+// The context for hook functions - can be typed with a service class
+export type HookContext = FeathersHookContext
diff --git a/server/src/hooks/log-error.ts b/server/src/hooks/log-error.ts
new file mode 100644
index 0000000..4010617
--- /dev/null
+++ b/server/src/hooks/log-error.ts
@@ -0,0 +1,18 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/log-error.html
+import type { HookContext, NextFunction } from '../declarations'
+import { logger } from '../logger'
+
+export const logError = async (context: HookContext, next: NextFunction) => {
+ try {
+ await next()
+ } catch (error: any) {
+ logger.error(error.stack)
+
+ // Log validation errors
+ if (error.data) {
+ logger.error('Data: %O', error.data)
+ }
+
+ throw error
+ }
+}
diff --git a/server/src/index.ts b/server/src/index.ts
new file mode 100644
index 0000000..dc4ec62
--- /dev/null
+++ b/server/src/index.ts
@@ -0,0 +1,11 @@
+import { app } from './app'
+import { logger } from './logger'
+
+const port = app.get('port')
+const host = app.get('host')
+
+process.on('unhandledRejection', (reason) => logger.error('Unhandled Rejection %O', reason))
+
+app.listen(port).then(() => {
+ logger.info(`Feathers app listening on http://${host}:${port}`)
+})
diff --git a/server/src/logger.ts b/server/src/logger.ts
new file mode 100644
index 0000000..3280e9e
--- /dev/null
+++ b/server/src/logger.ts
@@ -0,0 +1,10 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
+import { createLogger, format, transports } from 'winston'
+
+// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
+export const logger = createLogger({
+ // To see more detailed errors, change this to 'debug'
+ level: 'info',
+ format: format.combine(format.splat(), format.simple()),
+ transports: [new transports.Console()]
+})
diff --git a/server/src/services/index.ts b/server/src/services/index.ts
new file mode 100644
index 0000000..c32063b
--- /dev/null
+++ b/server/src/services/index.ts
@@ -0,0 +1,6 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/application.html#configure-functions
+import type { Application } from '../declarations'
+
+export const services = (app: Application) => {
+ // All services will be registered here
+}
diff --git a/server/src/sqlite.ts b/server/src/sqlite.ts
new file mode 100644
index 0000000..86b405a
--- /dev/null
+++ b/server/src/sqlite.ts
@@ -0,0 +1,17 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/databases.html
+import knex from 'knex'
+import type { Knex } from 'knex'
+import type { Application } from './declarations'
+
+declare module './declarations' {
+ interface Configuration {
+ sqliteClient: Knex
+ }
+}
+
+export const sqlite = (app: Application) => {
+ const config = app.get('sqlite')
+ const db = knex(config!)
+
+ app.set('sqliteClient', db)
+}
diff --git a/server/src/validators.ts b/server/src/validators.ts
new file mode 100644
index 0000000..015c65f
--- /dev/null
+++ b/server/src/validators.ts
@@ -0,0 +1,29 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html
+import { Ajv, addFormats } from '@feathersjs/schema'
+import type { FormatsPluginOptions } from '@feathersjs/schema'
+
+const formats: FormatsPluginOptions = [
+ 'date-time',
+ 'time',
+ 'date',
+ 'email',
+ 'hostname',
+ 'ipv4',
+ 'ipv6',
+ 'uri',
+ 'uri-reference',
+ 'uuid',
+ 'uri-template',
+ 'json-pointer',
+ 'relative-json-pointer',
+ 'regex'
+]
+
+export const dataValidator: Ajv = addFormats(new Ajv({}), formats)
+
+export const queryValidator: Ajv = addFormats(
+ new Ajv({
+ coerceTypes: true
+ }),
+ formats
+)
diff --git a/server/test/app.test.ts b/server/test/app.test.ts
new file mode 100644
index 0000000..0ac9848
--- /dev/null
+++ b/server/test/app.test.ts
@@ -0,0 +1,40 @@
+// For more information about this file see https://dove.feathersjs.com/guides/cli/app.test.html
+import assert from 'assert'
+import axios from 'axios'
+import type { Server } from 'http'
+import { app } from '../src/app'
+
+const port = app.get('port')
+const appUrl = `http://${app.get('host')}:${port}`
+
+describe('Feathers application tests', () => {
+ let server: Server
+
+ before(async () => {
+ server = await app.listen(port)
+ })
+
+ after(async () => {
+ await app.teardown()
+ })
+
+ it('starts and shows the index page', async () => {
+ const { data } = await axios.get(appUrl)
+
+ assert.ok(data.indexOf('') !== -1)
+ })
+
+ it('shows a 404 JSON error', async () => {
+ try {
+ await axios.get(`${appUrl}/path/to/nowhere`, {
+ responseType: 'json'
+ })
+ assert.fail('should never get here')
+ } catch (error: any) {
+ const { response } = error
+ assert.strictEqual(response?.status, 404)
+ assert.strictEqual(response?.data?.code, 404)
+ assert.strictEqual(response?.data?.name, 'NotFound')
+ }
+ })
+})
diff --git a/server/test/client.test.ts b/server/test/client.test.ts
new file mode 100644
index 0000000..1748a9d
--- /dev/null
+++ b/server/test/client.test.ts
@@ -0,0 +1,18 @@
+import assert from 'assert'
+import axios from 'axios'
+import type { Server } from 'http'
+import { app } from '../src/app'
+import { createClient } from '../src/client'
+
+import rest from '@feathersjs/rest-client'
+
+const port = app.get('port')
+const appUrl = `http://${app.get('host')}:${port}`
+
+describe('client tests', () => {
+ const client = createClient(rest(appUrl).axios(axios))
+
+ it('initialized the client', () => {
+ assert.ok(client)
+ })
+})
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644
index 0000000..0277dfb
--- /dev/null
+++ b/server/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "ts-node": {
+ "files": true
+ },
+ "compilerOptions": {
+ "target": "es2020",
+ "module": "commonjs",
+ "outDir": "./lib",
+ "rootDir": "./src",
+ "declaration": true,
+ "strict": true,
+ "esModuleInterop": true,
+ "sourceMap": true
+ },
+ "include": [
+ "src"
+ ],
+ "exclude": [
+ "test"
+ ]
+}
\ No newline at end of file
diff --git a/server/yarn.lock b/server/yarn.lock
new file mode 100644
index 0000000..fb57ccd
--- /dev/null
+++ b/server/yarn.lock
@@ -0,0 +1,4 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+