Coder Social home page Coder Social logo

absmach / magistrala Goto Github PK

View Code? Open in Web Editor NEW
2.4K 103.0 655.0 145.23 MB

Industrial IoT Messaging and Device Management Platform

Home Page: https://www.abstractmachines.fr/magistrala.html

License: Apache License 2.0

Go 98.00% Makefile 0.45% JavaScript 0.18% Shell 1.28% Dockerfile 0.06% HCL 0.03%
golang iot-platform edge-computing distributed-systems iot-cloud iot-gateway message-broker mqtt coap web-socket

magistrala's Introduction

Magistrala

Check License Header Check the consistency of generated files Continuous Delivery go report card coverage license chat

banner

Magistrala is modern, scalable, secure, open-source, and patent-free IoT cloud platform written in Go.

It accepts user and thing (sensor, actuator, application) connections over various network protocols (i.e. HTTP, MQTT, WebSocket, CoAP), thus making a seamless bridge between them. It is used as the IoT middleware for building complex IoT solutions.

For more details, check out the official documentation.

Features

  • Multi-protocol connectivity and bridging (HTTP, MQTT, WebSocket and CoAP)
  • Device management and provisioning (Zero Touch provisioning)
  • Mutual TLS Authentication (mTLS) using X.509 Certificates
  • Fine-grained access control (policies, ABAC/RBAC)
  • Message persistence (Cassandra, InfluxDB, MongoDB and PostgresSQL)
  • Platform logging and instrumentation support (Prometheus and OpenTelemetry)
  • Event sourcing
  • Container-based deployment using Docker and Kubernetes
  • LoRaWAN network integration
  • OPC UA integration
  • Edge Agent and Export services for remote IoT gateway management and edge computing
  • SDK
  • CLI
  • Small memory footprint and fast execution
  • Domain-driven design architecture, high-quality code and test coverage

Prerequisites

The following are needed to run Magistrala:

Developing Magistrala will also require:

Install

Once the prerequisites are installed, execute the following commands from the project's root:

docker compose -f docker/docker-compose.yml --env-file docker/.env -p git_github_com_absmach_magistrala_git_  up

This will bring up the Magistrala docker services and interconnect them. This command can also be executed using the project's included Makefile:

make run

If you want to run services from specific release checkout code from github and make sure that MG_RELEASE_TAG in .env is being set to match the release version

git checkout tags/<release_number> -b <release_number>
# e.g. `git checkout tags/0.13.0 -b 0.13.0`

Check that .env file contains:

MG_RELEASE_TAG=<release_number>

docker-compose should be used for development and testing deployments. For production we suggest using Kubernetes.

Usage

The quickest way to start using Magistrala is via the CLI. The latest version can be downloaded from the official releases page.

It can also be built and used from the project's root directory:

make cli
./build/cli version

Additional details on using the CLI can be found in the CLI documentation.

Documentation

Official documentation is hosted at Magistrala official docs page. Documentation is auto-generated, checkout the instructions on official docs repository:

If you spot an error or a need for corrections, please let us know - or even better: send us a PR.

Authors

Main architect and BDFL of Magistrala project is @drasko.

Additionally, @nmarcetic and @janko-isidorovic assured overall architecture and design, while @manuio and @darkodraskovic helped with crafting initial implementation and continuously worked on the project evolutions.

Besides them, Magistrala is constantly improved and actively developed by @anovakovic01, @dusanb94, @srados, @gsaleh, @blokovi, @chombium, @mteodor, @rodneyosodo and a large set of contributors.

Maintainers are listed in MAINTAINERS file.

The Magistrala team would like to give special thanks to @mijicd for his monumental work on designing and implementing a highly improved and optimized version of the platform, and @malidukica for his effort on implementing the initial user interface.

Professional Support

There are many companies offering professional support for the Magistrala system.

If you need this kind of support, best is to reach out to @drasko directly, and he will point you out to the best-matching support team.

Contributing

Thank you for your interest in Magistrala and the desire to contribute!

  1. Take a look at our open issues. The good-first-issue label is specifically for issues that are great for getting started.
  2. Checkout the contribution guide to learn more about our style and conventions.
  3. Make your changes compatible to our workflow.

We're Hiring

You like Magistrala and you would like to make it your day job? We're always looking for talented engineers interested in open-source, IoT and distributed systems. If you recognize yourself, reach out to @drasko - he will contact you back.

The best way to grab our attention is, of course, by sending PRs ๐Ÿ˜Ž.

Community

License

Apache-2.0

FOSSA Status

Data Collection for Magistrala

Magistrala is committed to continuously improving its services and ensuring a seamless experience for its users. To achieve this, we collect certain data from your deployments. Rest assured, this data is collected solely for the purpose of enhancing Magistrala and is not used with any malicious intent. The deployment summary can be found on our website.

The collected data includes:

  • IP Address - Used for approximate location information on deployments.
  • Services Used - To understand which features are popular and prioritize future developments.
  • Last Seen Time - To ensure the stability and availability of Magistrala.
  • Magistrala Version - To track the software version and deliver relevant updates.

We take your privacy and data security seriously. All data collected is handled in accordance with our stringent privacy policies and industry best practices.

Data collection is on by default and can be disabled by setting the env variable: MG_SEND_TELEMETRY=false

By utilizing Magistrala, you actively contribute to its improvement. Together, we can build a more robust and efficient IoT platform. Thank you for your trust in Magistrala!

magistrala's People

Contributors

1995parham avatar 1998-felix avatar anovakovic01 avatar arvindh123 avatar aryangodara avatar blokovi avatar buraksekili avatar charlie-jangala avatar chombium avatar darkodraskovic avatar dborovcanin avatar dependabot[bot] avatar drasko avatar fbugarski avatar ianmuchyri avatar jeffmboya avatar jonathandreyer avatar manuio avatar mfinley3 avatar mijicd avatar mteodor avatar musilah avatar nmarcetic avatar nwneisen avatar nyagamunene avatar rodneyosodo avatar sammyoina avatar sprql avatar tritao avatar washingtonkk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

magistrala's Issues

Mainflux CLI

A command line interface should be developed so we can test HTTP/MQTT and other protocol access. CLI program should be capable to send all commands to the Mainflux system that we are currently sending with curl or Postman.

NATS Debugging Tool

We need a small tool that connects to NATS and dumps the messages (probably NATS can do this), and also sends messages to NATS.

This is needed for debugging one server at the time. I.e. for debugging HTTP server we can receive REQs and send RSPs via this tool without the need to launch the whole composition with `core server, databases, etc...

LoRa Server <> Mainflux Adapter

Proposition is to connect LoRa Server: https://github.com/brocaar/loraserver with Mainflux server.

When we observe this picture: https://camo.githubusercontent.com/1b77c3663a5f695f488d2e30d87a7e42dde00109/68747470733a2f2f7777772e676c696666792e636f6d2f676f2f7075626c6973682f696d6167652f31313031303333392f4c2e706e67 Mainflux will come in the place here where is an "application" (the little cloud on the bottom right).

But for lora-app-server to talk to Mainflux we need to insert a small adapter (server) between them that will translate from one API to another.

I think best option will be to use an additional MQTT broker and just process MQTT pubs, redirecting them to appropriate endpoints.

Add `/identity` API Endpoint to Manager

We need identity API endpoint which verifies the token and decodes clientId from it and sends it as a response.

Other services (MQTT broker for example) will use this to check username/password that device provides on connection.

Getting Provisioning for the first time

I was using tutorial for running mainflux from here : http://mainflux.io/getting-started/#installation
Mainflux is running after "docker-compose up" but I can't get provisioning for device.

macbook-pro-de-uros-2-1:~ ukicar$ curl -s -S -i -X POST -H "Accept: application/json" -H "Content-Type: application/json" http://localhost:7070/devices | json | pygmentize -l json HTTP/1.1 201 Created Content-Type: application/json; charset=utf-8 Location: /devices/9bd13b96-5eaa-42ca-8b5b-1a8e835f9b07 Date: Thu, 13 Apr 2017 10:47:50 GMT Content-Length: 0

I'm not getting response from server, content-length is zero. Please help

Mainflux-mqtt exiting after sending an HTTP POST request.

When I tried this curl request as given in the tutorial, I got the output as follow:

Curl request:

curl -s -S -i -X POST -H "Accept: application/json" -H "Content-Type: application/json" http://localhost:7070/channels/fca48fe1-672a-424c-b219-a4fa6d62cc07/msg -d '[{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3987}]'

Output:

mainflux-core    | [negroni] Started POST /channels/fca48fe1-672a-424c-b219-a4fa6d62cc07/msg
mainflux-core    | Msg written
mainflux-core    | [negroni] Completed 202 Accepted in 1.057578ms
mainflux-mqtt    | Received a message: {"topic":"mainflux/channels/fca48fe1-672a-424c-b219-a4fa6d62cc07","publisher":"","payload":"W3siYm4iOiJzb21lLWJhc2UtbmFtZToiLCJidCI6MS4yNzYwMjAwNzYwMDFlKzA5LCAiYnUiOiJBIiwiYnZlciI6NSwgIm4iOiJ2b2x0YWdlIiwidSI6IlYiLCJ2IjoxMjAuMX0sIHsibiI6ImN1cnJlbnQiLCJ0IjotNSwidiI6MS4yfSwgeyJuIjoiY3VycmVudCIsInQiOi00LCJ2IjoxLjN9XQ=="}
mainflux-mqtt    | /opt/mainflux-mqtt/mainflux-mqtt.js:86
mainflux-mqtt    | 		payload: Buffer.from(atob(m.payload)),
mainflux-mqtt    | 		                ^
mainflux-mqtt    | 
mainflux-mqtt    | TypeError: this is not a typed array.
mainflux-mqtt    |     at Function.from (native)
mainflux-mqtt    |     at Object.callback (/opt/mainflux-mqtt/mainflux-mqtt.js:86:19)
mainflux-mqtt    |     at Client.processMsg (/opt/mainflux-mqtt/node_modules/nats/lib/nats.js:892:11)
mainflux-mqtt    |     at Client.processInbound (/opt/mainflux-mqtt/node_modules/nats/lib/nats.js:824:14)
mainflux-mqtt    |     at Socket.<anonymous> (/opt/mainflux-mqtt/node_modules/nats/lib/nats.js:421:12)
mainflux-mqtt    |     at emitOne (events.js:77:13)
mainflux-mqtt    |     at Socket.emit (events.js:169:7)
mainflux-mqtt    |     at readableAddChunk (_stream_readable.js:146:16)
mainflux-mqtt    |     at Socket.Readable.push (_stream_readable.js:110:10)
mainflux-mqtt    |     at TCP.onread (net.js:523:20)
mainflux-mqtt    | 2017/01/16 10:59:18 Command exited with error: exit status 1
mainflux-mqtt exited with code 1

I tried the request with Postman (chrome extension) and got the same result. I have to restart the docker instance to get the mqtt up and running again. What might be the reason behind this? Thanks for all the help in advance.

not able to start using Docker compose on windows

Description of the problem or steps to reproduce

followed normal installation instructions for docker.
this is the last few lines of the output
there was no error from starting other containers

mainflux-core    |                 == Industrial IoT System ==
mainflux-core    |
mainflux-core    |                 Made with <3 by Mainflux Team
mainflux-core    | [w] http://mainflux.io
mainflux-core    | [t] @mainflux
mainflux-core    |
mainflux-core    | Magic happens on port 7070
mainflux-core    | [negroni] Started GET /
mainflux-core    | [negroni] Completed 404 Not Found in 72.1ยตs
mainflux-core    | [negroni] Started GET /favicon.ico

and then i get 404 error, what could the reason be?

Specifications

PowerMac
Windows 10
Docker compose up command

Version

latest git clonable version

address already in use in compose up sequence

Followed tutorial for installation on Ubuntu Server 16.04 and getting this after "docker-compose up" command:

ERROR: for mainflux-mqtt Cannot start service mainflux-mqtt: driver failed programming external connectivity on endpoint mainflux-mqtt (37bf8034f6690e18a6ea95b7ed30d60e6726b369aa8cc02ed5e5c62cf745a070): Error starting userland proxy: listen tcp 0.0.0.0:1883: bind: address already in use
ERROR: Encountered errors while bringing up the project.

Specifications

Version

Cloned on May 30th 2017

Switch to monorepo

Migrate all Mainflux repos under a single umbrella repo - this one (mainflux/mainflux).

Enable Service Discovery - Consul or etcd

Add service discovery to Mainflux microservices - evaluate Consul and etcd and choose right candidate. Since Kubernetes uses it's version of etcd inaccessible by applications, it is irrelevant for K8 deployments if we choose Consul or etcd. Probably Consul would be better candidate here, due to the popularity and ease of use.

Evaluate EMQTT

EMQTT (http://emqtt.io/) looks like very interesting potential replacement for NATS.

Reasons are following:

  • We need at least one MQTT server - why then no use it as a main broker also
  • Massive scalability (Erlang shines here)
  • Multiple protocol support - WS is actually MQTT-over-WS but this should be more than enough for the start. Good news is CoAP support: emqx/emqx#510
  • With small HTTP API interface in front (from mainflux-http-server) and mainflux-core-server in the back this set-up should practically work out-of-the-box

We need to examine:

  • Auth - how do we handle security. We have to decide what of emqtt plugins to use (HTTP connection with Hydra?)
  • Should we eventually add all code as Erlang plugins

Enable TLS in Iris + Letsencrypt

This post mentiones Letsencrypt: https://evothings.com/evothings-does-mqtt-with-vernemq-or-emqtt/.
https://letsencrypt.org/ offers free TLS certificates.

Iris has a support: https://github.com/iris-contrib/examples/blob/master/letsencrypt/main.go#L17.

Basically, this function: https://github.com/kataras/iris/blob/master/iris.go#L515 callhs the function here: https://github.com/kataras/iris/blob/master/http.go#L406 which uses the code from this packet here: https://github.com/iris-contrib/letsencrypt

This has to be researched and we need to enable TLS on in Iris.

Implement SenML normallizer as a microservice

Adapters should not try to normalize SenML - they must forward "as-is" messages to NATS for other adapters to re-publish them to their subscribers.

Also, new microservice that normalizes SenML should be created as NodeJS does not have appropriate SenML processing libraries.

Finally, normalizes SenML should also be published on a separate topic, fro message-writer to consume it (as it expects a array of normalized SenML messages).

Switch to Go Standard Library for HTTP API

Based on these articles:
https://www.dougcodes.com/go-lang/gin-gonic-may-be-40x-faster-than-martini-but-it-is-not-better
https://stephensearles.com/three-reasons-you-should-not-use-martini/
https://codegangsta.io/blog/2014/05/19/my-thoughts-on-martini/
https://www.dougcodes.com/go-lang/martini-to-gin-back-to-martini-and-on-to-negroni-and-mux
https://github.com/urfave/negroni
http://0xdabbad00.com/2015/04/03/choosing_libraries_for_go_web_servers/
https://www.peterbe.com/plog/my-favorite-go-multiplexer
http://stackoverflow.com/questions/15240884/how-can-i-handle-http-requests-of-different-methods-to-in-go
http://stackoverflow.com/questions/16512009/how-to-extract-the-post-arguments-in-go-server

I am inclined to believe that Iris can be overkill that adds complexity (especially for testing) and is not Go idiomatic.

To get lean and Go idiomatic I propose use use of standard library as much as possible, especially that we are not building full stack here.

Standard lib seems to be a bit too lean regarding the multiplexer - you would switch on r.Method() inside the handler to discover if it was GET or POST for example (http://stackoverflow.com/questions/15240884/how-can-i-handle-http-requests-of-different-methods-to-in-go)

I prefer that simple look at one portion of router code tell us the handlers, so adding small idiomatic mux would be OK. Based on this I started implementation with Bone, but this can be easily inter-changed with Gorilla Mux.

Eventual addition of Negroni later can give us access to middlewares (if we evealute that it is needed)

Manager errors on existing user e-mail

BUG REPORT

  1. What were you trying to achieve?
    Create user

  2. What are the expected results?
    Location header with user UUID

  3. What are the received results?

drasko@Marx:~/go/src/github.com/mainflux/mainflux/cmd/manager$ go run main.go 
{"start":"manager","ts":"2017-09-24T02:08:36.020454561Z"}
{"http_port":8180,"ts":"2017-09-24T02:08:36.036257545Z"}
{"email":"[email protected]","error":"email already taken","method":"register","took":"96.534049ms","ts":"2017-09-24T02:09:36.875275927Z"}
2017/09/24 04:09:36 http: panic serving [::1]:58866: inconsistent label cardinality
goroutine 45 [running]:
net/http.(*conn).serve.func1(0xc420206000)
	/usr/local/go/src/net/http/server.go:1697 +0xd0
panic(0x783b20, 0xc42007ee50)
	/usr/local/go/src/runtime/panic.go:491 +0x283
github.com/mainflux/mainflux/vendor/github.com/prometheus/client_golang/prometheus.(*MetricVec).With(0xc4201868c0, 0xc42017ab70, 0xc4201923e8, 0x0)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/prometheus/client_golang/prometheus/vec.go:146 +0x76
github.com/mainflux/mainflux/vendor/github.com/prometheus/client_golang/prometheus.(*CounterVec).With(0xc42017c038, 0xc42017ab70, 0x2, 0xc42017ab70)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/prometheus/client_golang/prometheus/counter.go:145 +0x38
github.com/mainflux/mainflux/vendor/github.com/go-kit/kit/metrics/prometheus.(*Counter).Add(0xc4201a4f00, 0x3ff0000000000000)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/go-kit/kit/metrics/prometheus/prometheus.go:45 +0x5c
github.com/mainflux/mainflux/manager/api.(*metricService).Register.func1(0xc42017a330, 0xbe69e2d82e6aa02f, 0xe25929963, 0xa13440)
	/home/drasko/go/src/github.com/mainflux/mainflux/manager/api/metrics.go:30 +0xb9
github.com/mainflux/mainflux/manager/api.(*metricService).Register(0xc42017a330, 0xc4201e8240, 0x12, 0xc4201806f0, 0x3, 0x9d5fc0, 0xc42007f340)
	/home/drasko/go/src/github.com/mainflux/mainflux/manager/api/metrics.go:34 +0xcf
github.com/mainflux/mainflux/manager/api.registrationEndpoint.func1(0x9dc740, 0xc420186e80, 0x7a7000, 0xc4201a4d80, 0xc4201a4d80, 0x0, 0x0, 0x7c2f40)
	/home/drasko/go/src/github.com/mainflux/mainflux/manager/api/endpoint.go:13 +0x73
github.com/mainflux/mainflux/vendor/github.com/go-kit/kit/transport/http.Server.ServeHTTP(0xc420174900, 0x819d78, 0x819d98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x819d90, ...)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/go-kit/kit/transport/http/server.go:108 +0x338
github.com/mainflux/mainflux/vendor/github.com/go-kit/kit/transport/http.(*Server).ServeHTTP(0xc42014ed90, 0x9dbf80, 0xc4201840e0, 0xc4201b8b00)
	<autogenerated>:1 +0x94
github.com/mainflux/mainflux/vendor/github.com/go-zoo/bone.(*Route).parse(0xc4200a15e0, 0x9dbf80, 0xc4201840e0, 0xc4201b8b00, 0xc4200ce8c8)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/go-zoo/bone/route.go:148 +0x119
github.com/mainflux/mainflux/vendor/github.com/go-zoo/bone.(*Mux).parse(0xc4201728a0, 0x9dbf80, 0xc4201840e0, 0xc4201b8b00, 0x7e6280)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/go-zoo/bone/helper.go:18 +0xb9
github.com/mainflux/mainflux/vendor/github.com/go-zoo/bone.(*Mux).ServeHTTP(0xc4201728a0, 0x9dbf80, 0xc4201840e0, 0xc4201b8b00)
	/home/drasko/go/src/github.com/mainflux/mainflux/vendor/github.com/go-zoo/bone/bone.go:43 +0x4d
net/http.serverHandler.ServeHTTP(0xc4202025b0, 0x9dbf80, 0xc4201840e0, 0xc4201b8b00)
	/usr/local/go/src/net/http/server.go:2619 +0xb4
net/http.(*conn).serve(0xc420206000, 0x9dc740, 0xc420186d80)
	/usr/local/go/src/net/http/server.go:1801 +0x71d
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:2720 +0x288
  1. What are the steps to reproduce the issue?
curl -s -S -i -X POST -H "Content-Type: application/senml+json" localhost:8180/users -d '{"email":"[email protected]", "password":"123"}'

Create `mainflux-lite` - Compact Monolith

It would be great to remove NATS and bundle mainflux-http-server and mainflux-core-server in one monolith binary. This will greatly simplify things for development and testing (prototyping) deployments.

We could also remove InfluxDB from this binary and replace it by MongoDB for time-series data - so that we have just one DB with two collections (devices and data).

Go Server Configs and `docker-compose.yml`

New Go servers have to take some ENV config (devel, deployment, testing...). docker-compose.yml has to be corrected to properly share selected directories from local machine into the Docker containers.

In general re-write and test of docker-compose.yml is needed.

Config should go through ENV

We should be capable to change configs in docker-compose file. For this to be propagated into Docker best place IMHO is ENV.

We can take Hyperledger Fabric example. Here is docker-compose that starts node app: https://github.com/hyperledger/fabric/blob/master/examples/sdk/node/docker-compose.yml

What's interesting to note here is that not only ENV is defined in docker-compose, but command is pased from there also: https://github.com/hyperledger/fabric/blob/master/examples/sdk/node/docker-compose.yml#L45. This is because Dockerfile does only installation: https://github.com/hyperledger/fabric/blob/master/examples/sdk/node/Dockerfile and never executes command in the end.

This is nice way to change the way you are deploying app (i.e. command line parameters that you are passing) and not have then hard-coded in the Docker image.

Message Writer Problem

Hi,

I got this error when I want to install mainflux with docker. Whats the reason?

ERROR: repository mainflux/message-writer not found: does not exist or no pull access

Thanks


Starting NATS, Cassandra and Nginx...

Starting nats ... done
Starting cassandra ... done

Waiting for Cassandra to start. This takes time, please be patient...
.............OK

Starting Mainflux composition...

Pulling mqtt-adapter (mainflux/mqtt-adapter:latest)...
latest: Pulling from mainflux/mqtt-adapter
Digest: sha256:d17b25a10c1f5ad53d8865ceb714bca29e7f9179a4275596fa4ccaa25f577422
Status: Image is up to date for mainflux/mqtt-adapter:latest
Pulling manager (mainflux/manager:latest)...
latest: Pulling from mainflux/manager
Digest: sha256:8119f293e2c275df2c7008d1ab9db9fbdec6ea3ff59789f2c619d662bfba5543
Status: Image is up to date for mainflux/manager:latest
Pulling message-writer (mainflux/message-writer:latest)...
ERROR: repository mainflux/message-writer not found: does not exist or no pull access
Aborting due to errexit on line 122. Exit code: 1
Starting mqtt-adapter ... done
Starting manager ... done
Starting message-writer ... done
Starting http-adapter ... done

Starting Nginx...

Starting nginx ... done
ERROR: No containers to start
Aborting due to errexit on line 136. Exit code: 1

Stopping Nginx...

Stopping Mainflux composition...

Stopping mainflux-http ... done
Stopping mainflux-message-writer ... done

Stopping NATS and Cassandra...

Stopping mainflux-cassandra ... done
Stopping mainflux-nats ... done

*** MAINFLUX IS OFF ***

`device` deletion should trigger referencing `channel`s deletions

Current device model (https://github.com/Mainflux/mainflux-lite/blob/master/models/device.go, https://github.com/Mainflux/mainflux-lite/blob/master/models/deviceTemplate.json) has no reference to channels created for this device - only channel holds the referenced device Device ID: https://github.com/Mainflux/mainflux-lite/blob/master/models/channel.go, https://github.com/Mainflux/mainflux-lite/blob/master/models/channelTemplate.json

channel is accessed as localhost:7070/devices/<device_id>/channels/<channel_id>. When DELETE is issued on this URI, channel will be deleted. However - when DELETE is issued on localhost:7070/devices/<device_id> only device will be deleted, but not the channels that belog to this device - and we will not be able to access them anymore (since we will not have ID of the device anymore).

Do we have to keep list of channels in device struct? Xively holds the array channels inside device: http://developer.xively.com/assets/Xively_QS_TimeSeries.pdf. Not sure how M2X does it, but it is worh looking: https://m2x.att.com/developer/documentation/v2/device

How do we handle triggers in MongoDB?

Docker Fails on RPi

pi@raspberrypi:~/mainflux $ sudo docker-compose up
Starting mainflux-mongodb
Starting mainflux-emqttd
Starting mainflux-server
Attaching to mainflux-emqttd, mainflux-mongodb, mainflux-server
mainflux-mongodb   | panic: standard_init_linux.go:175: exec user process caused "exec format error" [recovered]
mainflux-mongodb   |    panic: standard_init_linux.go:175: exec user process caused "exec format error"
mainflux-mongodb   | 
mainflux-mongodb   | goroutine 1 [running, locked to thread]:
mainflux-mongodb   | panic(0x3339c8, 0x56e3b9c0)
mainflux-mongodb   |    /usr/local/go/src/runtime/panic.go:481 +0x330
mainflux-mongodb   | github.com/urfave/cli.HandleAction.func1(0x56e95958)
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:478 +0x328
mainflux-mongodb   | panic(0x3339c8, 0x56e3b9c0)
mainflux-mongodb   |    /usr/local/go/src/runtime/panic.go:443 +0x448
mainflux-mongodb   | github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization.func1(0x56e955e0, 0x56dc02a0, 0x56e95668)
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:259 +0x13c
mainflux-mongodb   | github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization(0x56e3c2a0, 0x464264b8, 0x56e3b9c0)
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:277 +0x498
mainflux-mongodb   | main.glob.func8(0x56e4a1e0, 0x0, 0x0)
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/main_unix.go:26 +0x58
mainflux-mongodb   | reflect.Value.call(0x2e90f8, 0x3efd4c, 0x13, 0x362250, 0x4, 0x56e95918, 0x1, 0x1, 0x0, 0x0, ...)
mainflux-mongodb   |    /usr/local/go/src/reflect/value.go:435 +0xeb4
mainflux-mongodb   | reflect.Value.Call(0x2e90f8, 0x3efd4c, 0x13, 0x56e95918, 0x1, 0x1, 0x0, 0x0, 0x0)
mainflux-mongodb   |    /usr/local/go/src/reflect/value.go:303 +0x84
mainflux-mongodb   | github.com/urfave/cli.HandleAction(0x2e90f8, 0x3efd4c, 0x56e4a1e0, 0x0, 0x0)
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:487 +0x230
mainflux-mongodb   | github.com/urfave/cli.Command.Run(0x364678, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ce288, 0x51, 0x0, ...)
mainflux-emqttd    | panic: standard_init_linux.go:175: exec user process caused "exec format error" [recovered]
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/command.go:191 +0xcd0
mainflux-mongodb   | github.com/urfave/cli.(*App).Run(0x56e26240, 0x56e08050, 0x2, 0x2, 0x0, 0x0)
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:240 +0x84c
mainflux-emqttd    |    panic: standard_init_linux.go:175: exec user process caused "exec format error"
mainflux-mongodb   | main.main()
mainflux-emqttd    | 
mainflux-mongodb   |    /go/src/github.com/opencontainers/runc/main.go:137 +0xbb4
mainflux-emqttd    | goroutine 1 [running, locked to thread]:
mainflux-emqttd    | panic(0x3339c8, 0x56ed5900)
mainflux-emqttd    |    /usr/local/go/src/runtime/panic.go:481 +0x330
mainflux-emqttd    | github.com/urfave/cli.HandleAction.func1(0x56f05958)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:478 +0x328
mainflux-emqttd    | panic(0x3339c8, 0x56ed5900)
mainflux-emqttd    |    /usr/local/go/src/runtime/panic.go:443 +0x448
mainflux-emqttd    | github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization.func1(0x56f055e0, 0x56e422d0, 0x56f05668)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:259 +0x13c
mainflux-emqttd    | github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization(0x56ebc300, 0x464a84b8, 0x56ed5900)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:277 +0x498
mainflux-emqttd    | main.glob.func8(0x56eb6320, 0x0, 0x0)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/main_unix.go:26 +0x58
mainflux-emqttd    | reflect.Value.call(0x2e90f8, 0x3efd4c, 0x13, 0x362250, 0x4, 0x56f05918, 0x1, 0x1, 0x0, 0x0, ...)
mainflux-emqttd    |    /usr/local/go/src/reflect/value.go:435 +0xeb4
mainflux-emqttd    | reflect.Value.Call(0x2e90f8, 0x3efd4c, 0x13, 0x56f05918, 0x1, 0x1, 0x0, 0x0, 0x0)
mainflux-emqttd    |    /usr/local/go/src/reflect/value.go:303 +0x84
mainflux-emqttd    | github.com/urfave/cli.HandleAction(0x2e90f8, 0x3efd4c, 0x56eb6320, 0x0, 0x0)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:487 +0x230
mainflux-emqttd    | github.com/urfave/cli.Command.Run(0x364678, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ce288, 0x51, 0x0, ...)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/command.go:191 +0xcd0
mainflux-emqttd    | github.com/urfave/cli.(*App).Run(0x56ea8240, 0x56e8a050, 0x2, 0x2, 0x0, 0x0)
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:240 +0x84c
mainflux-emqttd    | main.main()
mainflux-server    | panic: standard_init_linux.go:175: exec user process caused "exec format error" [recovered]
mainflux-emqttd    |    /go/src/github.com/opencontainers/runc/main.go:137 +0xbb4
mainflux-server    |    panic: standard_init_linux.go:175: exec user process caused "exec format error"
mainflux-server    | 
mainflux-server    | goroutine 1 [running, locked to thread]:
mainflux-server    | panic(0x3339c8, 0x56dd5c80)
mainflux-server    |    /usr/local/go/src/runtime/panic.go:481 +0x330
mainflux-server    | github.com/urfave/cli.HandleAction.func1(0x56e7d958)
mainflux-server    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:478 +0x328
mainflux-server    | panic(0x3339c8, 0x56dd5c80)
mainflux-server    |    /usr/local/go/src/runtime/panic.go:443 +0x448
mainflux-server    | github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization.func1(0x56e7d5e0, 0x56da8258, 0x56e7d668)
mainflux-server    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:259 +0x13c
mainflux-server    | github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization(0x56dd62a0, 0x4640e490, 0x56dd5c80)
mainflux-server    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:277 +0x498
mainflux-server    | main.glob.func8(0x56e241e0, 0x0, 0x0)
mainflux-server    |    /go/src/github.com/opencontainers/runc/main_unix.go:26 +0x58
mainflux-server    | reflect.Value.call(0x2e90f8, 0x3efd4c, 0x13, 0x362250, 0x4, 0x56e7d918, 0x1, 0x1, 0x0, 0x0, ...)
mainflux-server    |    /usr/local/go/src/reflect/value.go:435 +0xeb4
mainflux-server    | reflect.Value.Call(0x2e90f8, 0x3efd4c, 0x13, 0x56e7d918, 0x1, 0x1, 0x0, 0x0, 0x0)
mainflux-server    |    /usr/local/go/src/reflect/value.go:303 +0x84
mainflux-emqttd exited with code 2
mainflux-server    | github.com/urfave/cli.HandleAction(0x2e90f8, 0x3efd4c, 0x56e241e0, 0x0, 0x0)
mainflux-server    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:487 +0x230
mainflux-mongodb exited with code 2
mainflux-server    | github.com/urfave/cli.Command.Run(0x364678, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3ce288, 0x51, 0x0, ...)
mainflux-server    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/command.go:191 +0xcd0
mainflux-server    | github.com/urfave/cli.(*App).Run(0x56e16240, 0x56df0050, 0x2, 0x2, 0x0, 0x0)
mainflux-server    |    /go/src/github.com/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:240 +0x84c
mainflux-server    | main.main()
mainflux-server    |    /go/src/github.com/opencontainers/runc/main.go:137 +0xbb4
mainflux-server exited with code 2

Issues in docker demployement

After I docker-compose up, the micro services are started running. I can see the 'mainflux' welcome text logo. However, I am getting the following error,

Error: Uncaught, unspecified "error" event. (Could not connect to server: Error: connect ECONNREFUSED 127.0.0.1:4222)
mainflux-ws | at Client.emit (events.js:144:17)
mainflux-ws | at Socket. (/mainflux-ws/node_modules/nats/lib/nats.js:389:14)
mainflux-ws | at emitOne (events.js:77:13)
mainflux-ws | at Socket.emit (events.js:169:7)
mainflux-ws | at emitErrorNT (net.js:1253:8)
mainflux-ws | at doNTCallback2 (node.js:441:9)
mainflux-ws | at process._tickCallback (node.js:355:17)
mainflux-ws | [09:53:57] [nodemon] app crashed - waiting for file changes before starting...

Also not sure how to view/access IOT platform on the browser. No such getting started document available on the wiki page.

TIA,
Prakash.

Webpack failed to compile - Mainflux

Hi,
My problem is about mainflux-ui. More precisely, i cannt run the UI due to the following error:
Can some one help me how can i fix this?

siavash@ubuntu:~/Desktop/Mainflux/mainflux-ui$ sudo ng serve
[sudo] password for siavash:
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200 **
Date: 2017-08-15T09:59:04.143Z
Hash: 67952886eeacdc6b6f7f
Time: 86139ms
chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {main} main.bundle.js, main.bundle.js.map (main) 53 kB {vendor} [initial] [rendered]
chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 87.8 kB {inline} [initial] [rendered]
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 5.11 MB [initial] [rendered]

ERROR in /home/siavash/Desktop/Mainflux/mainflux-ui/node_modules/@angular/cdk/typings/rxjs/rx-operators.d.ts (11,10): Module '"/home/siavash/Desktop/Mainflux/mainflux-ui/node_modules/rxjs/Scheduler"' has no exported member 'IScheduler'.

webpack: Failed to compile.

CONTRIBUTING.md javasript style link is dead

Hi,

I stared "playing" with Mainflux and when I was checking the contributing guide and I saw that the link to the javascript guidelines is dead.

Can someone please take a look at it?

Suggestion:
For the JavaScript/Node projects the pre-commit can be used to run a style check with automatic corrections and eventually the unit tests if there are any. This package attaches a pre-commit hook and blocks the committing to the repo if some of the checks (run scripts) .
This way it can be assured that the code is always well formatted, the unit tests pass, a linter is run and code coverage is checked.

Best Regards,
Jovan

add filter to api channels

Add a time frame to channels api
To request http://<mainfluxServer>:7070/channels/<channelId>?filter:starttime=xxxxx,endtime=yyyyy
This should return a json with from channel id with entries only in the time frame given in the api

Docker Composition Fails on CentOS


[root@samgps mainflux]# docker-compose up
Starting mainflux-mongodb
Starting mainflux-emqttd
Starting mainflux-server
Attaching to mainflux-emqttd, mainflux-mongodb, mainflux-server
mainflux-server    | panic: Network Error : dial tcp [::1]:1883: getsockopt: connection refused
mainflux-server    |
mainflux-server    | goroutine 1 [running]:
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=18a7c813e3d5
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] db version v3.2.10
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] git version: 79d9b3ab5ce20f51c272b4411202710a082d0317
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.1t  3 May 2016
mainflux-server    | panic(0x701f40, 0xc420166e60)
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] allocator: tcmalloc
mainflux-server    |    /usr/local/go/src/runtime/panic.go:500 +0x1a1
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] modules: none
mainflux-server    | github.com/mainflux/mainflux/clients.(*MqttConn).MqttSub(0xc42013dee0)
mainflux-mongodb   | 2016-10-17T14:52:18.119+0000 I CONTROL  [initandlisten] build environment:
mainflux-server    |    /go/src/github.com/mainflux/mainflux/clients/mqtt_client.go:61 +0x322
mainflux-mongodb   | 2016-10-17T14:52:18.120+0000 I CONTROL  [initandlisten]     distmod: debian81
mainflux-server    | main.main()
mainflux-mongodb   | 2016-10-17T14:52:18.120+0000 I CONTROL  [initandlisten]     distarch: x86_64
mainflux-server    |    /go/src/github.com/mainflux/mainflux/main.go:126 +0x928
mainflux-mongodb   | 2016-10-17T14:52:18.120+0000 I CONTROL  [initandlisten]     target_arch: x86_64
mainflux-mongodb   | 2016-10-17T14:52:18.120+0000 I CONTROL  [initandlisten] options: { storage: { journal: { enabled: false }, mmapv1: { smallFiles: true } } }
mainflux-mongodb   | 2016-10-17T14:52:18.139+0000 I -        [initandlisten] Detected data files in /data/db created by the 'wiredTiger' storage engine, so setting the active storage engine to 'wiredTiger'.
mainflux-mongodb   | 2016-10-17T14:52:18.139+0000 I STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=13G,session_max=20000,eviction=(threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),,log=(enabled=false),
mainflux-mongodb   | 2016-10-17T14:52:18.351+0000 W STORAGE  [initandlisten] Detected configuration for non-active storage engine mmapv1 when current storage engine is wiredTiger
mainflux-mongodb   | 2016-10-17T14:52:18.351+0000 I CONTROL  [initandlisten]
mainflux-mongodb   | 2016-10-17T14:52:18.351+0000 I CONTROL  [initandlisten] ** WARNING: You are running on a NUMA machine.
mainflux-mongodb   | 2016-10-17T14:52:18.351+0000 I CONTROL  [initandlisten] **          We suggest launching mongod like this to avoid performance problems:
mainflux-mongodb   | 2016-10-17T14:52:18.351+0000 I CONTROL  [initandlisten] **              numactl --interleave=all mongod [other options]
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten]
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten]
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
mainflux-mongodb   | 2016-10-17T14:52:18.352+0000 I CONTROL  [initandlisten]
mainflux-mongodb   | 2016-10-17T14:52:18.353+0000 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data'
mainflux-mongodb   | 2016-10-17T14:52:18.353+0000 I NETWORK  [HostnameCanonicalizationWorker] Starting hostname canonicalization worker
mainflux-mongodb   | 2016-10-17T14:52:18.354+0000 I NETWORK  [initandlisten] waiting for connections on port 27017
mainflux-mongodb   | 2016-10-17T14:52:18.476+0000 I NETWORK  [initandlisten] connection accepted from 172.17.0.4:33071 #1 (1 connection now open)
mainflux-mongodb   | 2016-10-17T14:52:18.495+0000 I NETWORK  [conn1] end connection 172.17.0.4:33071 (0 connections now open)
mainflux-server exited with code 2
mainflux-emqttd    | emqttd is started successfully!
mainflux-emqttd    | emqttd_auth_http
mainflux-emqttd    | RPC to '[email protected]' failed: {'EXIT',
mainflux-emqttd    |                                    {badarg,
mainflux-emqttd    |                                     [{ets,match,
mainflux-emqttd    |                                       [mqttd_ctl_cmd,{{'_',plugins},'$1','_'}],
mainflux-emqttd    |                                       []},
mainflux-emqttd    |                                      {emqttd_ctl,lookup,1,
mainflux-emqttd    |                                       [{file,"src/emqttd_ctl.erl"},{line,78}]},
mainflux-emqttd    |                                      {emqttd_ctl,run,1,
mainflux-emqttd    |                                       [{file,"src/emqttd_ctl.erl"},{line,70}]},
mainflux-emqttd    |                                      {rpc,'-handle_call_call/6-fun-0-',5,
mainflux-emqttd    |                                       [{file,"rpc.erl"},{line,206}]}]}}
mainflux-emqttd exited with code 1

Implement MQTT Auth

MQTT Auth should be implemented - device auth token to be sent to manager service for auth on every CONNECT or PUB/SUB.

Evaluate Ladon for ACL

https://github.com/ory-am/ladon

Can it be usefull to us or is it overkill?

Can this be used for one user to share his device with some other user (creating a group of users who have access to this DeviceID resource or just giving the user access to this resource without grouping them).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.