Merge pull request #403 from RickCarlino/monday

Documentation, FBJS updates, maintenance
This commit is contained in:
Rick Carlino 2017-08-14 15:41:37 -05:00 committed by GitHub
commit 89e9200ac2
62 changed files with 301 additions and 616 deletions

View file

@ -3,7 +3,9 @@
If you want to run a server on a LAN for personal use, this is the easiest and cheapest option.
**Simplicity:** :heart::heart::heart:
**Reliability:** :broken_heart:
**Affordability:** :heart::heart::heart:
1. Follow the [developer setup guide](https://github.com/FarmBot/Farmbot-Web-App#developer-setup).
@ -13,10 +15,12 @@ If you want to run a server on a LAN for personal use, this is the easiest and c
**DEPRECATION NOTICE / PULL REQUESTS WELCOME**: We no longer deploy the server using Dokku. The instructions related to MariaDB are out of date (we use Postgresql now). **If you wish to use Dokku** we would be happy to help you along the way. Please raise an issue if you would like to help with updating the deployment docs.
**Simplicity:** :broken_heart:
**Reliability:** :heart::heart:
**Affordability:** :heart::heart:
0. Provision a fresh Ubuntu 16 server. We recommend DigitalOcean's "Ubuntu 16 docker" image. Make sure you have at least 1gb of memory. **Don't use the Dokku image that Digital Ocean provides**. It is out of date and will not support this application.
0. Provision a fresh Ubuntu 16 server. We recommend DigitalOcean's "Ubuntu 16 Docker" image. Make sure you have at least 1gb of memory. **Don't use the Dokku image that Digital Ocean provides**. It is out of date and will not support this application.
1. [Install the latest version of Dokku onto the machine](https://github.com/dokku/dokku#installing)
2. Visit the server's URL in a browser. Follow the directions on screen to setup Dokku.
3. `git remote add my_server dokku@my_server_name:my_app_name`
@ -42,7 +46,9 @@ If you want to run a server on a LAN for personal use, this is the easiest and c
# Deployment Using Heroku (good)
**Simplicity:** :heart::heart::heart::heart:
**Reliability:** :heart::heart::heart::heart:
**Affordability:** :broken_heart:
1. Deploy as you would normally [deploy to Heroku](https://devcenter.heroku.com/articles/getting-started-with-rails4#deploy-your-application-to-heroku)
@ -62,7 +68,7 @@ Wait until you see this message and **DO NOT CONTINUE**:
```
Make sure your web server displays the following content at
http://yourdomain.io/.well-known/acme-challenge/SOME-LONG-URL before continuing:
http://YOUR_DOMAIN/.well-known/acme-challenge/SOME-LONG-URL before continuing:
ya6k1edW38z-CopyThisValueNow!!!
@ -77,7 +83,7 @@ You should see this:
```
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/yourdomain.io/fullchain.pem
/etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
```
**Heroku Users**: Run the following snippet to submit the certs to Heroku:
@ -85,19 +91,16 @@ IMPORTANT NOTES:
**First time:**
```
heroku certs:add /etc/letsencrypt/live/yourdomain.io/fullchain.pem /etc/letsencrypt/live/yourdomain.io/privkey.pem
heroku certs:add /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
```
**After that:**
```
sudo heroku certs:update /etc/letsencrypt/live/staging.farmbot.io/fullchain.pem /etc/letsencrypt/live/staging.farmbot.io/privkey.pem --app=farmbot-staging
sudo heroku certs:update /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem --app=HEROKU_APP_NAME
```
Heroku will then give you instructions on which DNS records you must create.
**Dokku Users**: FarmBot, Inc. no longer uses Dokku. As such, our ability to troubleshoot problems is limited.
Please [raise an issue](https://github.com/FarmBot/Farmbot-Web-App/issues/new) to receive community support.
# Renew SSL Certificates
1. Run `sudo certbot certonly --manual -d YOUR_DOMAIN_HERE`
@ -106,7 +109,7 @@ Please [raise an issue](https://github.com/FarmBot/Farmbot-Web-App/issues/new) t
```
Make sure your web server displays the following content at
http://staging.farmbot.io/.well-known/acme-challenge/<CODE_HERE> before continuing:
http://YOUR_DOMAIN/.well-known/acme-challenge/<CODE_HERE> before continuing:
3tFAi5c7tJK-YOURS\_WILL\_BE\_DIFFERENT
@ -119,7 +122,7 @@ heroku config:set ACME_SECRET=THAT_BIG_CODE_FROM_PREVIOUS_STEP --app=YOUR_APP_HE
```
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/staging.farmbot.io/fullchain.pem.
/etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem.
```
4. (Heroku users only) `sudo heroku certs:update /etc/letsencrypt/live/YOUR_DOMAIN_HERE/fullchain.pem /etc/letsencrypt/live/YOUR_DOMAIN_HERE/privkey.pem`

View file

@ -14,7 +14,7 @@ If you are a developer interested in contributing or would like to provision you
# Q: What is the Farmbot Web App?
This repo contains the web based user interface and RESTful JSON API for Farmbot. This includes things like storage of user data, plant data, authorization tokens and a variety of other resources.
This repo contains FarmBot's web based user interface, as well as a RESTful JSON API. The API stores data such as user account information, plant data, authorization tokens and a variety of other resources.
The key responsibility of the API is *information and permissions management*. This should not be confused with device control, which is done via [MQTT](https://github.com/FarmBot/mqtt-gateway).
@ -40,13 +40,13 @@ You will need the following:
0. `cd Farmbot-Web-App`
0. `bundle install`
0. `yarn install`
0. **MOST IMPORTANT STEP**. Copy `config/database.example.yml` to `config/database.yml`. In GNU/Linux or Mac: `mv config/database.example.yml config/database.yml`. **Please read the instructions inside the file. Replace the example values provided with real world values.**
0. **MOST IMPORTANT STEP**. Copy `config/database.example.yml` to `config/database.yml` via `mv config/database.example.yml config/database.yml`. **Please read the instructions inside the file. Replace the example values provided with real world values.**
0. Give permission to create a database*
0. `rake db:create:all db:migrate db:seed`
0. (optional) Verify installation with `RAILS_ENV=test rake db:create db:migrate && rspec spec` (API) and `npm run test` (Frontend).
0. Start server with `npm run dev`. Make sure you set an `MQTT_HOST` entry in `application.yml` pointing to the IP address or domain of the (soon-to-be-installed) MQTT server. You will need to set that up next.
0. Now that the API server is running, [provision an MQTT server](https://github.com/FarmBot/mqtt-gateway).
0. Open [localhost:3808](http://localhost:3808). The application is now ready for use.
0. Open [localhost:3000](http://localhost:3000).
0. Although you can now try things out in your browser, you will still need to [provision an MQTT server](https://github.com/FarmBot/mqtt-gateway) before you can control a FarmBot.
0. [Raise an issue](https://github.com/FarmBot/Farmbot-Web-App/issues/new?title=Installation%20Failure) if you hit problems with any of these steps. *We can't fix issues we don't know about.*
\*Give permission to `user` to create database:

View file

@ -1,5 +1,6 @@
class DashboardController < ApplicationController
ACME_SECRET = ENV["ACME_SECRET"]
NO_ENV = "NO ACME_SECRET SET"
ACME_SECRET = ENV["ACME_SECRET"] || "NO ACME_SECRET SET"
LONG_REVISION = ENV["BUILT_AT"] || ENV["HEROKU_SLUG_COMMIT"] || "NONE"
$FRONTEND_SHARED_DATA = { NODE_ENV: Rails.env || "development",
TOS_URL: ENV.fetch("TOS_URL", ""),

View file

@ -1,5 +0,0 @@
# THIS SHOULD NOT EXIST.
class ToolBay < ApplicationRecord
belongs_to :device
has_many :tool_slots
end

View file

@ -3,11 +3,7 @@
# etc.
class ToolSlot < ApplicationRecord
belongs_to :tool
belongs_to :tool_bay # <== DELETE THIS ASAP!!!!
has_one :point, as: :pointer#, dependent: :destroy
# has_many :sequence_dependencies, dependent: :destroy, as: :dependency
validates_uniqueness_of :tool,
allow_blank: true,
allow_nil: true,
message: "already in use by another tool slot"
has_one :point, as: :pointer
IN_USE = "already in use by another tool slot"
validates_uniqueness_of :tool, allow_blank: true, allow_nil: true, message: IN_USE
end

View file

@ -10,6 +10,7 @@ window.globalConfig = <%= raw($FRONTEND_SHARED_DATA) %> // SEE COMMENTS!;
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i" rel="stylesheet">
<% if Rails.env.development? && %>
<script src="http://<%= ENV["API_HOST"] %>:8081/target/target-script-min.js#anonymous"></script>
<% if Rails.env.development? %>
<script async src="http://<%= ENV["API_HOST"] %>:8081/target/target-script-min.js#anonymous">
</script>
<% end %>

View file

@ -1,7 +1,6 @@
FarmBot::Application.routes.draw do
namespace :api, defaults: {format: :json}, constraints: { format: 'json' } do
resources :tool_bays, only: [:index]
resources :images, only: [:create, :destroy, :show, :index]
resources :regimens, only: [:create, :destroy, :index, :update]
resources :peripherals, only: [:create, :destroy, :index, :update]
@ -40,16 +39,10 @@ FarmBot::Application.routes.draw do
# =======================================================================
# NON-API (USER FACING) URLS:
# =======================================================================
get "/" => 'dashboard#front_page',
as: :front_page
get "/app" => 'dashboard#main_app',
as: :dashboard
match "/app/*path",
to: 'dashboard#main_app',
via: :all
get "/" => 'dashboard#front_page', as: :front_page
get "/app" => 'dashboard#main_app', as: :dashboard
match "/app/*path", to: 'dashboard#main_app', via: :all
get "/password_reset/*token" => 'dashboard#password_reset',
as: :password_reset
get "/verify" => 'dashboard#verify',
as: :verify
get "/verify" => 'dashboard#verify', as: :verify
end

View file

@ -1,115 +0,0 @@
class CreateEverything < ActiveRecord::Migration[4.2]
def self.up
execute """
ALTER TABLE schedules
ADD CONSTRAINT check_schedules_time_unit_naming
CHECK (time_unit IN (minutely hourly daily weekly monthly yearly) )
"""
execute """
ALTER TABLE regimens
ADD CONSTRAINT check_regimens_color_naming
CHECK (color IN (blue green yellow orange purple pink gray red) )
"""
end
def self.down
execute """
ALTER TABLE schedules
DROP CONSTRAINT check_schedules_time_unit_naming
"""
execute """
ALTER TABLE regimens
DROP CONSTRAINT check_regimens_color_naming
"""
end
def change
create_table :devices do |t|
t.integer :planting_area_id
t.string :uuid
t.string :name
end
create_table :plants do |t|
t.integer :device_id
t.integer :planting_area_id
t.string :name
t.string :img_url
t.string :icon_url
t.string :openfarm_slug
t.string :x
t.string :y
t.string :planted_at
end
create_table :planting_areas do |t|
t.integer :width
t.integer :length
t.integer :device_id
end
create_table :regimens do |t|
t.string :color
t.string :name
t.integer :device_id
end
create_table :regimen_items do |t|
t.integer :time_offset
t.integer :schedule_id
t.integer :regimen_id
t.integer :sequence_id
end
create_table :schedules do |t|
t.integer :sequence_id
t.integer :device_id
t.datetime :start_time
t.datetime :end_time
t.datetime :next_time
t.integer :repeat
# minutely hourly daily weekly monthly yearly
t.string :time_unit
end
create_table :sequences do |t|
t.integer :schedule_id
t.integer :device_id
t.string :regimen
t.string :name
t.string :color
end
create_table :steps do |t|
t.integer :sequence_id
t.string :message_type
t.integer :position
t.text :command
end
### A single system User on the decision support system.
create_table :users do |t|
t.integer :device_id
t.string :name
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.datetime :remember_created_at
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end

View file

@ -1,9 +0,0 @@
class ConvertXYToFloatAndRemoveScheduleIdFromRegimenItem < ActiveRecord::Migration[4.2]
def change
# Make Plant x/y a float
[:x,:y].each do |coord|
remove_column :plants, coord
add_column :plants, coord, :float, default: 0
end
end
end

View file

@ -1,5 +0,0 @@
class MakeRegimenItemOffsetBigger < ActiveRecord::Migration[4.2]
def change
change_column :regimen_items, :time_offset, :integer, limit: 8
end
end

View file

@ -1,5 +0,0 @@
class AddWebcamUrlToDevices < ActiveRecord::Migration[4.2]
def change
add_column :devices, :webcam_url, :string
end
end

View file

@ -1,7 +0,0 @@
class ConvertPlantedAtToDatetime < ActiveRecord::Migration[4.2]
def change
# Make Plant planted_at a datetime
remove_column :plants, :planted_at
add_column :plants, :planted_at, :datetime
end
end

View file

@ -1,10 +0,0 @@
class CreateStepParams < ActiveRecord::Migration[4.2]
def change
create_table :step_params do |t|
t.string :key
t.string :value
t.belongs_to :step, index: true, foreign_key: true
remove_column :steps, :command, :text
end
end
end

View file

@ -1,6 +0,0 @@
class RemoveScheduleIdFromSequence < ActiveRecord::Migration[4.2]
def change
remove_column :sequences, :schedule_id, :integer
remove_column :sequences, :regimen, :string
end
end

View file

@ -1,6 +0,0 @@
class DropStepsAndStepParams < ActiveRecord::Migration[4.2]
def change
drop_table :step_params
drop_table :steps
end
end

View file

@ -1,7 +0,0 @@
class AddBodyArgsAndKindToSequence < ActiveRecord::Migration[4.2]
def change
add_column :sequences, :kind, :string, default: "sequence"
add_column :sequences, :args, :text
add_column :sequences, :body, :text
end
end

View file

@ -1,5 +0,0 @@
class RemoveScheduleIdFromRegimenItems < ActiveRecord::Migration[4.2]
def change
remove_column :regimen_items, :schedule_id, :integer
end
end

View file

@ -1,5 +0,0 @@
class RemoveUuidFromDevice < ActiveRecord::Migration[4.2]
def change
remove_column :devices, :uuid, :integer
end
end

View file

@ -1,12 +0,0 @@
class CreateSequenceDependencies < ActiveRecord::Migration[4.2]
def change
create_table :sequence_dependencies do |t|
t.references :dependency, polymorphic: true
t.references :sequence
end
add_index :sequence_dependencies, :dependency_id
add_index :sequence_dependencies, :dependency_type
add_index :sequence_dependencies, :sequence_id
end
end

View file

@ -1,12 +0,0 @@
class CreatePeripherals < ActiveRecord::Migration[4.2]
def change
create_table :peripherals do |t|
t.references :device, index: true, foreign_key: true
t.integer :pin
t.integer :mode
t.string :label
t.timestamps null: false
end
end
end

View file

@ -1,22 +0,0 @@
class CreateDelayedJobs < ActiveRecord::Migration[4.2]
def self.up
create_table :delayed_jobs, force: true do |table|
table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
table.text :handler, null: false # YAML-encoded string of the object that will do work
table.text :last_error # reason for last failure (See Note below)
table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
table.datetime :locked_at # Set when a client is working on this object
table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
table.string :locked_by # Who is working on this object (if locked)
table.string :queue # The name of the queue this job is in
table.timestamps null: true
end
add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
end
def self.down
drop_table :delayed_jobs
end
end

View file

@ -1,24 +0,0 @@
class CreateToolStuff < ActiveRecord::Migration[5.0]
def change
create_table :tool_bays do |t|
t.references :device, foreign_key: true
t.string :name
t.timestamps
end
create_table :tool_slots do |t|
t.references :tool_bay, foreign_key: true
t.string :name
t.integer :x
t.integer :y
t.integer :z
t.timestamps
end
create_table :tools do |t|
t.references :tool_slot, foreign_key: true
t.string :name
t.timestamps
end
end
end

View file

@ -1,11 +0,0 @@
class CreateTokenExpirations < ActiveRecord::Migration[5.0]
def change
create_table :token_expirations do |t|
t.string :sub
t.integer :exp
t.string :jti
t.timestamps
end
end
end

View file

@ -1,5 +0,0 @@
class AddDeviceIdToTools < ActiveRecord::Migration[5.0]
def change
add_reference :tools, :device, index: true
end
end

View file

@ -1,14 +0,0 @@
class CreateLogs < ActiveRecord::Migration[5.0]
def change
create_table :logs do |t|
t.text :message
t.text :meta
t.text :channels
t.integer :device_id
t.timestamps
end
add_index :logs, :device_id
add_column :devices, :max_log_count, :integer, default: 100
end
end

View file

@ -1,7 +0,0 @@
class FlipFlopToolsRelation < ActiveRecord::Migration[5.0]
def change
remove_column :tools, :tool_slot_id, :integer
add_column :tool_slots, :tool_id, :integer
add_index :tool_slots, :tool_id
end
end

View file

@ -1,7 +0,0 @@
class RemoveLegacyDeviseColumnsFromUserTable < ActiveRecord::Migration[5.0]
def change
remove_column :users, :reset_password_token, :string
remove_column :users, :reset_password_sent_at, :datetime
remove_column :users, :remember_created_at, :datetime
end
end

View file

@ -1,12 +0,0 @@
class AddVerifiedAtToUsersTable < ActiveRecord::Migration[5.0]
def up
add_column :users, :verified_at, :datetime
add_column :users, :verification_token, :string
User.update_all(verified_at: Time.now)
end
def down
remove_column :users, :verified_at, :datetime
remove_column :users, :verification_token, :string
end
end

View file

@ -1,13 +0,0 @@
class CreateImages < ActiveRecord::Migration[5.0]
def change
create_table :images do |t|
t.integer :device_id
t.text :meta
t.datetime :attachment_processed_at
t.timestamps
end
add_index :images, :device_id
add_attachment :images, :attachment
end
end

View file

@ -1,15 +0,0 @@
class AddMissingDatabaseIndexes < ActiveRecord::Migration[5.0]
def change
add_index :planting_areas, :device_id
add_index :regimen_items, :regimen_id
add_index :regimen_items, :sequence_id
add_index :schedules, :sequence_id
add_index :schedules, :device_id
add_index :sequences, :device_id
add_index :regimens, :device_id
add_index :devices, :planting_area_id
add_index :plants, :planting_area_id
add_index :plants, :device_id
add_index :users, :device_id
end
end

View file

@ -1,8 +0,0 @@
class AddMaxImageCountToDevices < ActiveRecord::Migration[5.0]
def change
add_column :devices,
:max_images_count,
:integer,
default: Device::DEFAULT_MAX_IMAGES
end
end

View file

@ -1,11 +0,0 @@
class ChangeSchedulesIntoFarmEvents < ActiveRecord::Migration[5.0]
def change
remove_column :schedules, :sequence_id, :integer
rename_table :schedules, :farm_events
add_column :farm_events, :repeats, :boolean
add_reference :farm_events,
:executable,
polymorphic: true,
index: true
end
end

View file

@ -1,9 +0,0 @@
class AddTimestampsToPlant < ActiveRecord::Migration[5.0]
def change
add_column :plants, :created_at, :datetime
add_index :plants, :created_at
remove_column :plants, :planted_at, :integer
change_column :plants, :x, :integer
change_column :plants, :y, :integer
end
end

View file

@ -1,7 +0,0 @@
class AddTimestampsToSequence < ActiveRecord::Migration[5.0]
def change
add_column :sequences, :updated_at, :datetime
add_column :sequences, :created_at, :datetime
add_index :sequences, :created_at
end
end

View file

@ -1,6 +0,0 @@
class AddAgreedToTermsToUser < ActiveRecord::Migration[5.0]
def change
add_column :users, :agreed_to_terms_at, :datetime
add_index :users, :agreed_to_terms_at
end
end

View file

@ -1,17 +0,0 @@
class CreatePoints < ActiveRecord::Migration[5.0]
def change
enable_extension "hstore"
create_table :points do |t|
t.float :radius
t.float :x
t.float :y
t.float :z
t.references :device, foreign_key: true
t.hstore :meta
t.timestamps
end
add_index :points, :meta, using: :gin
end
end

View file

@ -1,6 +0,0 @@
class AddRadiusToPlants < ActiveRecord::Migration[5.0]
def change
add_column :plants, :radius, :float, default: 50
remove_column :farm_events, :repeats, :boolean
end
end

View file

@ -1,6 +0,0 @@
class AddTimezoneToDevices < ActiveRecord::Migration[5.0]
def change
add_column :devices, :timezone, :string
add_index :devices, :timezone
end
end

View file

@ -1,7 +0,0 @@
class AddMissingForeignKeyConstraints < ActiveRecord::Migration[5.0]
def change
bad_tools = (ToolSlot.pluck(:tool_id) - Tool.pluck(:id)).compact.uniq.sort
ToolSlot.where(tool_id: bad_tools).destroy_all
add_foreign_key :tool_slots, :tools
end
end

View file

@ -1,10 +0,0 @@
class GetOldIconsOutOfTheDb < ActiveRecord::Migration[5.0]
def change
Plant
.where(radius: 50)
.update_all(radius: 25)
Plant
.where(icon_url: "/app-resources/img/icons/Sprout-96.png")
.update_all(icon_url: "IRELLEVANT_NOW")
end
end

View file

@ -1,11 +0,0 @@
class CleanUpOldDatabaseEntries < ActiveRecord::Migration[5.0]
def change
remove_column :plants, :img_url
remove_column :plants, :icon_url
remove_column :plants, :planting_area_id
remove_column :devices, :planting_area_id
remove_column :devices, :timezone
remove_column :farm_events, :next_time
drop_table :planting_areas
end
end

View file

@ -1,59 +0,0 @@
class NormalizePoints < ActiveRecord::Migration[5.0]
def change
# UPDATES TO TABLES ===================================================
change_column :points, :x, :float, null: false
change_column :points, :y, :float, null: false
change_column :points, :z, :float, null: false, default: 0
change_column :points, :radius, :float, null: false, default: 50
change_column :points, :device_id, :integer,null: false
change_column :sequences, :name, :string, null: false
change_column_null :sequence_dependencies, :sequence_id, false
add_foreign_key :sequence_dependencies,
:sequences,
column: :sequence_id
change_column :plants,
:openfarm_slug,
:string,
null: false,
default: 50
create_table(:generic_pointers) { |_| /# Empty table..#/ }
add_column :points, :name, :string, null: false, default: "untitled"
add_reference :points,
:pointer,
index: true,
polymorphic: true
# MANUAL MIGRATIONS ===================================================
ToolSlot.find_each do |ts|
Point.create!(x: ts[:x] || 0,
y: ts[:y] || 0,
z: ts[:z] || 0,
name: ts[:name] || "Untitled Tool Slot" ,
device_id: ts.tool_bay[:device_id],
pointer: ts,
meta: {})
end
Plant.find_each do |pl|
Point.create!(x: pl[:x] || 0,
y: pl[:y] || 0,
z: pl[:z] || 0,
name: pl[:openfarm_slug] || "unknown",
device_id: pl[:device_id],
pointer: pl,
meta: {})
end
# DESTRUCTIVE ACTIONS =================================================
[:x,:y].each do |coord|
remove_column :plants, coord, :float
remove_column :tool_slots, coord, :float
end
remove_column :plants, :device_id, :integer
remove_column :plants, :name, :string
remove_column :plants, :radius, :float
remove_column :tool_slots, :name, :string
remove_column :tool_slots, :tool_bay_id, :float
remove_column :tool_slots, :z, :float
drop_table :tool_bays
end
end

View file

@ -1,12 +0,0 @@
class EvolveToolSlotDeps < ActiveRecord::Migration[5.0]
def change
SequenceDependency.transaction do
# ToolSlots are merged with points now.
SequenceDependency
.where(dependency_type: "ToolSlot")
.map do |sd|
sd.update_attributes!(dependency: sd.dependency.point)
end
end
end
end

View file

@ -1,9 +0,0 @@
class AddPolymorphicConstraints < ActiveRecord::Migration[5.1]
def change
add_polymorphic_constraints :pointer,
:points,
polymorphic_models: [:plant,
:tool_slot,
:generic_pointer]
end
end

View file

@ -1,13 +0,0 @@
class CreateLogDispatches < ActiveRecord::Migration[5.1]
def change
create_table :log_dispatches do |t|
t.references :device, foreign_key: true
t.references :log, foreign_key: true
t.datetime :sent_at
t.timestamps
end
add_index :log_dispatches, :sent_at
end
end

View file

@ -1,8 +0,0 @@
class AddNonNullConstraintToPointsTable < ActiveRecord::Migration[5.1]
def change
Point.where(pointer_type: nil).destroy_all
Point.where(pointer_id: nil).destroy_all
change_column :points, :pointer_type, :string, null: false
change_column :points, :pointer_id, :integer, null: false
end
end

View file

@ -1,6 +0,0 @@
class AddTimezoneColumnBack < ActiveRecord::Migration[5.1]
def change
add_column :devices, :timezone, :string
add_index :devices, :timezone
end
end

View file

@ -1,5 +0,0 @@
class ChangeDefaultRadius < ActiveRecord::Migration[5.1]
def change
change_column :points, :radius, :float, default: 25
end
end

View file

@ -0,0 +1,205 @@
class InitSchema < ActiveRecord::Migration[5.1]
def up
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "hstore"
create_table "delayed_jobs", id: :serial, force: :cascade do |t|
t.integer "priority", default: 0, null: false
t.integer "attempts", default: 0, null: false
t.text "handler", null: false
t.text "last_error"
t.datetime "run_at"
t.datetime "locked_at"
t.datetime "failed_at"
t.string "locked_by"
t.string "queue"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
end
create_table "devices", id: :serial, force: :cascade do |t|
t.string "name"
t.string "webcam_url"
t.integer "max_log_count", default: 100
t.integer "max_images_count", default: 100
t.string "timezone"
t.index ["timezone"], name: "index_devices_on_timezone"
end
create_table "farm_events", id: :serial, force: :cascade do |t|
t.integer "device_id"
t.datetime "start_time"
t.datetime "end_time"
t.integer "repeat"
t.string "time_unit"
t.string "executable_type"
t.integer "executable_id"
t.index ["device_id"], name: "index_farm_events_on_device_id"
t.index ["executable_type", "executable_id"], name: "index_farm_events_on_executable_type_and_executable_id"
end
create_table "generic_pointers", id: :serial, force: :cascade do |t|
end
create_table "images", id: :serial, force: :cascade do |t|
t.integer "device_id"
t.text "meta"
t.datetime "attachment_processed_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "attachment_file_name"
t.string "attachment_content_type"
t.integer "attachment_file_size"
t.datetime "attachment_updated_at"
t.index ["device_id"], name: "index_images_on_device_id"
end
create_table "log_dispatches", force: :cascade do |t|
t.bigint "device_id"
t.bigint "log_id"
t.datetime "sent_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["device_id"], name: "index_log_dispatches_on_device_id"
t.index ["log_id"], name: "index_log_dispatches_on_log_id"
t.index ["sent_at"], name: "index_log_dispatches_on_sent_at"
end
create_table "logs", id: :serial, force: :cascade do |t|
t.text "message"
t.text "meta"
t.text "channels"
t.integer "device_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["device_id"], name: "index_logs_on_device_id"
end
create_table "peripherals", id: :serial, force: :cascade do |t|
t.integer "device_id"
t.integer "pin"
t.integer "mode"
t.string "label"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["device_id"], name: "index_peripherals_on_device_id"
end
create_table "plants", id: :serial, force: :cascade do |t|
t.string "openfarm_slug", default: "50", null: false
t.datetime "created_at"
t.index ["created_at"], name: "index_plants_on_created_at"
end
create_table "points", id: :serial, force: :cascade do |t|
t.float "radius", default: 25.0, null: false
t.float "x", null: false
t.float "y", null: false
t.float "z", default: 0.0, null: false
t.integer "device_id", null: false
t.hstore "meta"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name", default: "untitled", null: false
t.string "pointer_type", null: false
t.integer "pointer_id", null: false
t.index ["device_id"], name: "index_points_on_device_id"
t.index ["meta"], name: "index_points_on_meta", using: :gin
t.index ["pointer_type", "pointer_id"], name: "index_points_on_pointer_type_and_pointer_id"
end
create_table "regimen_items", id: :serial, force: :cascade do |t|
t.bigint "time_offset"
t.integer "regimen_id"
t.integer "sequence_id"
t.index ["regimen_id"], name: "index_regimen_items_on_regimen_id"
t.index ["sequence_id"], name: "index_regimen_items_on_sequence_id"
end
create_table "regimens", id: :serial, force: :cascade do |t|
t.string "color"
t.string "name"
t.integer "device_id"
t.index ["device_id"], name: "index_regimens_on_device_id"
end
create_table "sequence_dependencies", id: :serial, force: :cascade do |t|
t.string "dependency_type"
t.integer "dependency_id"
t.integer "sequence_id", null: false
t.index ["dependency_id"], name: "index_sequence_dependencies_on_dependency_id"
t.index ["dependency_type"], name: "index_sequence_dependencies_on_dependency_type"
t.index ["sequence_id"], name: "index_sequence_dependencies_on_sequence_id"
end
create_table "sequences", id: :serial, force: :cascade do |t|
t.integer "device_id"
t.string "name", null: false
t.string "color"
t.string "kind", default: "sequence"
t.text "args"
t.text "body"
t.datetime "updated_at"
t.datetime "created_at"
t.index ["created_at"], name: "index_sequences_on_created_at"
t.index ["device_id"], name: "index_sequences_on_device_id"
end
create_table "token_expirations", id: :serial, force: :cascade do |t|
t.string "sub"
t.integer "exp"
t.string "jti"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "tool_slots", id: :serial, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "tool_id"
t.index ["tool_id"], name: "index_tool_slots_on_tool_id"
end
create_table "tools", id: :serial, force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "device_id"
t.index ["device_id"], name: "index_tools_on_device_id"
end
create_table "users", id: :serial, force: :cascade do |t|
t.integer "device_id"
t.string "name"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "verified_at"
t.string "verification_token"
t.datetime "agreed_to_terms_at"
t.index ["agreed_to_terms_at"], name: "index_users_on_agreed_to_terms_at"
t.index ["device_id"], name: "index_users_on_device_id"
t.index ["email"], name: "index_users_on_email", unique: true
end
add_foreign_key "log_dispatches", "devices"
add_foreign_key "log_dispatches", "logs"
add_foreign_key "peripherals", "devices"
add_foreign_key "points", "devices"
add_foreign_key "sequence_dependencies", "sequences"
add_foreign_key "tool_slots", "tools"
end
def down
raise ActiveRecord::IrreversibleMigration, "The initial migration is not revertable"
end
end

View file

@ -0,0 +1,13 @@
class SquasherClean < ActiveRecord::Migration[5.1]
class SchemaMigration < ActiveRecord::Base
end
def up
migrations = Dir.glob(File.join(File.dirname(__FILE__), '*.rb'))
versions = migrations.map { |file| File.basename(file)[/\A\d+/] }
SchemaMigration.where("version NOT IN (?)", versions).delete_all
end
def down
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170807143633) do
ActiveRecord::Schema.define(version: 20170814084814) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -107,7 +107,7 @@ ActiveRecord::Schema.define(version: 20170807143633) do
end
create_table "points", id: :serial, force: :cascade do |t|
t.float "radius", default: 25.0, null: false
t.float "radius", default: 50.0, null: false
t.float "x", null: false
t.float "y", null: false
t.float "z", default: 0.0, null: false

View file

@ -53,7 +53,7 @@
"deep-freeze": "^0.0.1",
"enzyme": "^2.9.1",
"extract-text-webpack-plugin": "^2.1.2",
"farmbot": "4.3.8",
"farmbot": "4.3.9",
"farmbot-toastr": "^1.0.3",
"fastclick": "^1.0.6",
"file-loader": "^0.11.2",

View file

@ -0,0 +1,12 @@
require 'spec_helper'
describe DashboardController do
include Devise::Test::ControllerHelpers
describe 'ACME endpoint' do
it 'has a fallback' do
process :lets_encrypt, method: :get, params: { id: "FOO" }
expect(response.body).to include(DashboardController::NO_ENV)
end
end
end

View file

@ -11,4 +11,7 @@ describe Sequence do
expect { Sequence.create!(optns) }.to raise_error(ActiveRecord::RecordInvalid)
end
it "picks random values" do
3.times { expect(Sequence.random).to be_kind_of(Sequence) }
end
end

View file

@ -35,7 +35,10 @@ export let bot: Everything["bot"] = {
},
"pins": {},
"configuration": {},
"informational_settings": {},
"informational_settings": {
busy: false,
locked: false
},
"user_env": {},
"process_info": {
"farmwares": {}

View file

@ -6,13 +6,16 @@ import { mapStateToProps } from "./state_to_props";
import { WebcamPanel } from "./webcam_panel";
import { Props } from "./interfaces";
import { Move } from "./move";
import * as _ from "lodash";
@connect(mapStateToProps)
export class Controls extends React.Component<Props, {}> {
render() {
// TODO: Add this to interface in FBJS - RC 10-aug-17
let arduinoBusy = _.get(this.props.bot.hardware.informational_settings, "busy", false);
let arduinoBusy = !!this
.props
.bot
.hardware
.informational_settings
.busy;
return (
<Page className="controls">
<Row>

View file

@ -3,7 +3,6 @@ import { t } from "i18next";
import { emergencyLock, emergencyUnlock } from "../actions";
import { EStopButtonProps } from "../interfaces";
import { SyncStatus } from "farmbot/dist";
import { get } from "lodash";
// Leave this here. Type checker will notify us if we ever need to change
// this string.
const LOCKED: SyncStatus = "locked";
@ -12,8 +11,7 @@ export class EStopButton extends React.Component<EStopButtonProps, {}> {
render() {
let i = this.props.bot.hardware.informational_settings;
let { sync_status } = i;
// TODO: ADD `.locked` to FBJS interface!
let lock1 = get(i, "locked", false);
let lock1 = !!i.locked;
let lock2 = sync_status === LOCKED;
let isLocked = lock1 || lock2;
let toggleEmergencyLock = isLocked ? emergencyUnlock : emergencyLock;

View file

@ -59,8 +59,6 @@ export let OsUpdateButton = ({ bot }: BotProp) => {
<ToggleButton toggleValue={toggleVal}
toggleAction={() => {
let os_auto_update = !osUpdateBool ? 1 : 0;
// TODO: This no longer needs to be a thunk
// since it does not change redux state.
updateConfig({ os_auto_update })(noop);
}} />
</Col>

View file

@ -61,7 +61,10 @@ export let initialState: BotState = {
},
pins: {},
configuration: {},
informational_settings: {},
informational_settings: {
busy: false,
locked: false
},
user_env: {},
process_info: {
farmwares: {},

View file

@ -12,14 +12,16 @@ import {
import { PlantPointer } from "../interfaces";
import { SlotWithTool } from "../resources/interfaces";
import { BotPosition } from "../devices/interfaces";
import { isNumber } from "lodash";
/** TODO: Use Enums */
export type BotOriginQuadrant = 1 | 2 | 3 | 4;
export type ZoomLevelPayl = 0.1 | -0.1;
export function isBotOriginQuadrant(mystery: any):
type Mystery = BotOriginQuadrant | number | undefined;
export function isBotOriginQuadrant(mystery: Mystery):
mystery is BotOriginQuadrant {
return [1, 2, 3, 4].includes(mystery);
return isNumber(mystery) && [1, 2, 3, 4].includes(mystery);
}
export interface State {

View file

@ -19,21 +19,21 @@ export class FarmbotColorPicker extends React.Component<FarmbotPickerProps, {}>
}
hueCSS = (): React.CSSProperties => {
// TODO: Investigate if this is a bug with our code or @types/react:
// CC @Chris
let position: any = "relative";
let width = "100%";
let paddingBottom = "10%";
let overflow: any = "hidden";
return { position, width, paddingBottom, overflow };
return {
position: "relative",
width: "100%",
paddingBottom: "10%",
overflow: "hidden"
};
}
saturationCSS = (): React.CSSProperties => {
let position: any = "relative";
let width = "100%";
let paddingBottom = "35%";
let overflow: any = "hidden";
return { position, width, paddingBottom, overflow };
return {
position: "relative",
width: "100%",
paddingBottom: "35%",
overflow: "hidden"
};
}
hueboxCSS = (): React.CSSProperties => {
@ -79,7 +79,7 @@ export class FarmbotColorPicker extends React.Component<FarmbotPickerProps, {}>
<div style={{ width: "100%", paddingBottom: "15%" }} />
<div style={this.hueCSS()}>
<Hue
{...dontTouchThis as any}
{...dontTouchThis}
pointer={this.customPointer}
onChange={_.noop} />
<div style={this.hueboxCSS()} />
@ -87,7 +87,7 @@ export class FarmbotColorPicker extends React.Component<FarmbotPickerProps, {}>
<div style={{ width: "100%", paddingBottom: "2%" }} />
<div style={this.saturationCSS()}>
<Saturation
{...dontTouchThis as any}
{...dontTouchThis}
pointer={this.customPointer}
onChange={_.noop} />
<div style={this.saturationboxCSS()} />

View file

@ -1958,9 +1958,9 @@ farmbot-toastr@^1.0.0, farmbot-toastr@^1.0.3:
farmbot-toastr "^1.0.0"
typescript "^2.3.4"
farmbot@4.3.8:
version "4.3.8"
resolved "https://registry.yarnpkg.com/farmbot/-/farmbot-4.3.8.tgz#740f4c3c694df738b4ac8acda10eea8f472d23da"
farmbot@4.3.9:
version "4.3.9"
resolved "https://registry.yarnpkg.com/farmbot/-/farmbot-4.3.9.tgz#fdf1ac0f6165a52ab7f6b2d4cd4dfdc21f5b7892"
dependencies:
mqtt "^1.7.4"
typescript "^2.4.2"