Compare commits

..

330 Commits

Author SHA1 Message Date
Girish Ramakrishnan e09d2db7e6 Fix layout on larger screen 2018-06-05 22:46:06 -07:00
Girish Ramakrishnan 28f183f450 Explicitly mark zone name as optional 2018-06-05 21:14:10 -07:00
Girish Ramakrishnan 5126b605f2 Remove redundant "This domain must be hosted" text
If anything we can add them as <sup> info links
2018-06-04 21:47:23 -07:00
Girish Ramakrishnan aaebdda9d6 Remove bizzare color 2018-06-04 21:46:29 -07:00
Girish Ramakrishnan af29a3f498 Remove redundant "This domain must be hosted" text 2018-06-04 21:44:15 -07:00
Girish Ramakrishnan 55b6773d88 Add labels to setup page as well 2018-06-04 21:39:09 -07:00
Girish Ramakrishnan 239ec86c4a Fix setupdns layout 2018-06-04 20:59:23 -07:00
Girish Ramakrishnan 13adca00d6 do not center restore page labels 2018-06-04 20:46:40 -07:00
Girish Ramakrishnan a76631ee3d Add labels
Poeple seem to forget wat those fields are for mid-way through filling
the form. The place holder stuff is not overly obvious apparently.

This makes it consistent with all our other UI nevertheless
2018-06-04 20:30:22 -07:00
Girish Ramakrishnan 7ac99f16cd Fallback -> Self-signed 2018-06-04 16:52:17 -07:00
Girish Ramakrishnan 3b6ca1c59d Fix spacing 2018-06-04 15:51:29 -07:00
Girish Ramakrishnan 3f55064c47 Fix name.com links 2018-06-04 10:13:47 -07:00
Girish Ramakrishnan 917bc2a88c Reduce the padding (like in configure dialog) 2018-05-30 10:08:36 -07:00
Johannes Zellner 85dfa1ccad Always show the restore and clone tabs 2018-05-30 15:13:05 +02:00
Johannes Zellner 606828da1d Show backup create button next to the tabs 2018-05-30 15:07:16 +02:00
Johannes Zellner a182d78566 Give busy feedback when the app clone is initiated 2018-05-29 22:53:51 +02:00
Johannes Zellner 0f294531d3 Do not show backup creation button if we still fetch backups 2018-05-29 22:48:42 +02:00
Johannes Zellner 9189532b83 Fix error reporting of restore form
angular does not attach form elements to the $scope when the DOM
elements are created dynamically!
2018-05-29 22:46:06 +02:00
Johannes Zellner c031253bd4 Use tabs instead of accordion for restore/clone ui 2018-05-29 22:05:18 +02:00
Johannes Zellner 11ae5d4832 Allow the user to setup a subscription when enabling email 2018-05-29 17:17:53 +02:00
Johannes Zellner 3251dc3d73 Show hint to setup subscription if more than allowed apps are installed 2018-05-28 20:26:24 +02:00
Girish Ramakrishnan a671e6acf7 Move the buttons to dialog footer 2018-05-28 10:05:57 -07:00
Girish Ramakrishnan 5ce658125c Make the text less verbose 2018-05-28 10:05:52 -07:00
Girish Ramakrishnan 1d88a935a5 Add clone UI
part of #248
2018-05-28 10:05:47 -07:00
Girish Ramakrishnan 8da07a16b9 dnsConfig is used incorrectly 2018-05-28 10:05:42 -07:00
Girish Ramakrishnan 7ce045ae51 Make it an accordion 2018-05-28 10:05:36 -07:00
Girish Ramakrishnan a3e253436e Allow setting app visibility for non-SSO apps
Fixes #532
2018-05-25 13:59:53 -07:00
Johannes Zellner 08955ce5a4 Do not show cryptic UTC timestamp format for backups 2018-05-25 13:56:15 +02:00
Johannes Zellner 57a4fa2d38 fix the restore dropdown and change some labels 2018-05-25 13:49:38 +02:00
Johannes Zellner 548e652ba2 Update the setup subscription dialog text 2018-05-25 13:39:04 +02:00
Girish Ramakrishnan e92cfae4d9 Fix error handling 2018-05-24 20:15:08 -07:00
Girish Ramakrishnan ed1320c937 Only send mailboxName if it changed 2018-05-24 16:29:36 -07:00
Girish Ramakrishnan abccffd05f Put domain in the mailbox name
part of cloudron/box#558
2018-05-24 16:05:27 -07:00
Girish Ramakrishnan c1106aa32e Save -> Configure 2018-05-24 15:54:50 -07:00
Girish Ramakrishnan a94f5daac9 Allow mailbox name to be configured
part of cloudron/box#558
2018-05-24 15:42:40 -07:00
Girish Ramakrishnan 9d9f16e948 Re-work the configure dialog 2018-05-24 14:13:00 -07:00
Girish Ramakrishnan 0b5bd0b4cd Another take on this layout 2018-05-24 13:43:25 -07:00
Girish Ramakrishnan caa59dd9a9 Better layout 2018-05-24 09:55:33 -07:00
Girish Ramakrishnan 423958dd0e Add links to appstore 2018-05-24 09:45:08 -07:00
Girish Ramakrishnan 6a90cf5102 Fix incoming email section a bit 2018-05-24 09:26:41 -07:00
Johannes Zellner 2d820a3005 Remove label about free trial 2018-05-24 15:23:33 +02:00
Johannes Zellner 7fff55a1ed Style the tab widget to fit our style 2018-05-24 15:17:44 +02:00
Girish Ramakrishnan 52d1d47030 Add setting for mail from validation
Fixes #454
2018-05-23 23:34:11 -07:00
Girish Ramakrishnan 7df1d388f0 ManageSieve is now STARTTLS 2018-05-23 23:15:17 -07:00
Girish Ramakrishnan a34f5f13da Remove panel headers since they appear very heavy 2018-05-23 22:12:09 -07:00
Girish Ramakrishnan bcc1e5f79c Make the email UI tabbed 2018-05-23 22:00:48 -07:00
Girish Ramakrishnan 44d32ea281 add app backup button
Fixes cloudron/box#497
2018-05-23 21:17:12 -07:00
Johannes Zellner 63e16c9bb8 Minor rewording to better fit the free tier 2018-05-22 19:17:14 +02:00
Girish Ramakrishnan 528be0e4c0 Remove redundant auto-update check button 2018-05-21 09:28:30 -07:00
Girish Ramakrishnan 0660a924b7 Add some spacing 2018-05-19 18:11:20 -07:00
Girish Ramakrishnan 6f29d5f3f6 explicitZone is gone 2018-05-19 17:28:43 -07:00
Girish Ramakrishnan 9903004af5 Remove trial expiry display 2018-05-18 22:30:39 -07:00
Girish Ramakrishnan 0c2b250901 Fix article 2018-05-18 12:08:19 -07:00
Girish Ramakrishnan 00f8e96dd2 Remove spurious > 2018-05-17 09:06:27 -07:00
Girish Ramakrishnan 267fa79164 Add advanced settings to setupdns 2018-05-15 16:24:09 -07:00
Girish Ramakrishnan 1528aa9d0c domain: add advanced view for zone name and cert provider 2018-05-15 15:39:43 -07:00
Girish Ramakrishnan 0cd6f7f2e7 Set text based on debugMode
Fixes cloudron/box#553
2018-05-15 12:13:31 -07:00
Johannes Zellner f05967d871 Fix terminal resize on window resize
This still does not update the terminal COLUMNS and ROWS
2018-05-15 18:31:54 +02:00
Girish Ramakrishnan 26087e1580 remove user/ from profile route 2018-05-13 21:51:13 -07:00
Girish Ramakrishnan 9275c4fbfd Fix display of error message in mailbox add 2018-05-11 11:16:38 -07:00
Johannes Zellner a2e03ccf7a Fix the dns check notification link 2018-05-11 11:05:28 +02:00
Johannes Zellner 6605a38eab Add scheduler addon dropdown to webterminal 2018-05-11 10:40:11 +02:00
Johannes Zellner 0a09d89684 Add name.com to setup dns 2018-05-09 18:44:03 +02:00
Johannes Zellner c0d4100dd1 Add name.com DNS provider 2018-05-09 12:24:46 +02:00
Girish Ramakrishnan 791f5af3e0 Fix scope use in logs.js and terminal.js 2018-05-07 14:48:52 -07:00
Girish Ramakrishnan ed57e701bc Revert "route53: set listHostedZonesByName property"
This reverts commit 07b428f051.

We can just do this in the backend entirely
2018-05-07 13:41:36 -07:00
Girish Ramakrishnan c678a9b6d7 lint 2018-05-07 13:18:51 -07:00
Girish Ramakrishnan 07b428f051 route53: set listHostedZonesByName property 2018-05-07 11:27:24 -07:00
Girish Ramakrishnan a1ab8b6aa8 reset the expect dns records on refresh 2018-05-06 23:47:38 -07:00
Girish Ramakrishnan a07848164c Show dialog when disabling email 2018-05-06 23:41:39 -07:00
Girish Ramakrishnan 1b1d4ee431 Add GoDaddy 2018-05-06 22:36:33 -07:00
Girish Ramakrishnan f8e5668c5c typo 2018-05-06 21:51:58 -07:00
Girish Ramakrishnan d8719626d9 Add UI for Gandi LiveDNS
Part of cloudron/box#235
2018-05-06 19:38:30 -07:00
Girish Ramakrishnan 3a06797de0 AppStore now returns 401 2018-05-06 19:38:30 -07:00
Girish Ramakrishnan b9d7149dbb Remove email domain logic from users view 2018-05-01 15:25:43 -07:00
Girish Ramakrishnan 72bbb4ec68 Use the scope to determine what the user has access to 2018-05-01 14:40:59 -07:00
Girish Ramakrishnan d9ec1be9b6 Get the user profile first to determine the scope 2018-05-01 14:19:05 -07:00
Girish Ramakrishnan ecddb6977a Use the version in the status API instead
This is because we want to get the user's profile as the first
thing to know the scopes
2018-05-01 14:15:12 -07:00
Girish Ramakrishnan 77220038a1 clients API has moved up a level 2018-05-01 11:08:34 -07:00
Girish Ramakrishnan 016f194271 typo 2018-04-29 18:02:48 -07:00
Girish Ramakrishnan e34fecee5e Pass scope and expiresAt as body params 2018-04-28 22:02:46 -07:00
Girish Ramakrishnan 7448dc5ec5 Capitalize 2018-04-28 10:19:21 -07:00
Girish Ramakrishnan bfd25a08c2 Use new route to query user apps 2018-04-26 20:07:53 -07:00
Girish Ramakrishnan 8861e61bdf profile routes have moved under /api/v1/user 2018-04-26 19:58:38 -07:00
Girish Ramakrishnan 049c2fca8a Make it a button 2018-04-26 09:58:45 -07:00
Girish Ramakrishnan 63df9df913 Add links to TOTP apps 2018-04-26 09:32:56 -07:00
Johannes Zellner 05b6740e07 Refresh the user profile on 2fa status change 2018-04-26 16:54:03 +02:00
Johannes Zellner 46aac0288c Add 2fa disable flow 2018-04-26 16:38:26 +02:00
Johannes Zellner 4ec0fbd33c Show 2fa status in accounts view 2018-04-26 16:32:43 +02:00
Johannes Zellner 7a24d5fdfa Add 2fa enabling flow to accounts view 2018-04-26 16:30:07 +02:00
Johannes Zellner 3f082ccace Fix typo 2018-04-26 14:49:14 +02:00
Girish Ramakrishnan a37fc3093a Add note to setup subscription 2018-04-26 01:19:48 -07:00
Johannes Zellner 4541940a76 Make the setup billing button more descriptive 2018-04-24 11:49:03 +02:00
Johannes Zellner 3017fe0c95 Remove (in trial) label 2018-04-24 11:47:47 +02:00
Johannes Zellner d3bf9a2478 Avoid further flickering of the subscription buttons 2018-04-24 11:47:00 +02:00
Johannes Zellner 7107672358 Fix trial badge flickering 2018-04-23 20:55:03 +02:00
Johannes Zellner 8519d6665e Add support for appstore 2fa setup 2018-04-22 18:52:37 +02:00
Girish Ramakrishnan 774c9e435e Make it 1.5 since we have some swap to work with 2018-04-19 19:20:45 -07:00
Girish Ramakrishnan e1f35a8d9f Fix wording 2018-04-18 12:40:30 -07:00
Johannes Zellner 67136e418c Encode the email query argument
This is required to correctly transfer characters like +
2018-04-18 21:29:40 +02:00
Johannes Zellner 924cc997aa Hide cloudron.io account section in settings if it is not yet registered 2018-04-18 21:18:31 +02:00
Johannes Zellner 12eda5f507 Improve subscription indicator
We now don't show anything when the cloudron is still in trial period
but the user already has setup billing
2018-04-18 17:56:04 +02:00
Girish Ramakrishnan 96bb979abf Add CRM category 2018-04-17 16:28:31 -07:00
Girish Ramakrishnan f74ad0323c Fix README title 2018-04-17 09:39:32 -07:00
Girish Ramakrishnan 62fd73f1b1 Fix link 2018-04-17 09:39:02 -07:00
Johannes Zellner 44f027eb04 remove unused require 2018-04-15 11:00:25 +02:00
Johannes Zellner c9cf6d610b Revision is not part of OAuth credentials 2018-04-15 11:00:01 +02:00
Johannes Zellner 16d4d28046 Be consistent and make domain list entries edit on click 2018-04-13 21:50:21 +02:00
Johannes Zellner 2280008029 Give a lot more backlog for the app logs 2018-04-13 13:13:51 +02:00
Johannes Zellner a36439314d Fix logviewer autoscroll for chrome
Chrome does not support the scrollTopMax property
2018-04-13 13:12:32 +02:00
Johannes Zellner 290b44fbb7 Fixup catchall to use mailboxes instead of users 2018-04-12 13:02:32 +02:00
Johannes Zellner 404c280595 Catchall property is now addresses 2018-04-12 12:37:32 +02:00
Johannes Zellner b0f8370a31 Make copies of mailbox properties to work with in edit dialog
This avoids local data model updates in the UI on dialog cancel
2018-04-12 12:26:54 +02:00
Johannes Zellner 6abcf4ec3c Remove left over artifacts from inline edit mode 2018-04-12 12:15:23 +02:00
Johannes Zellner db6d7bcefb Refresh the mailinglists on edit 2018-04-12 12:12:22 +02:00
Johannes Zellner 0e1913b0b4 Handle corner case, where mailbox referenced in mailinglist got removed 2018-04-12 12:12:11 +02:00
Johannes Zellner cc6b097dc5 Use a copy of the mailinglist properties on edit 2018-04-12 12:02:20 +02:00
Johannes Zellner c4f7a0c857 Ensure new mailboxes appear in maillinglist options 2018-04-12 11:57:15 +02:00
Johannes Zellner 34187d76b6 Update Chart.js to new version and show full fqdn for apps 2018-04-12 11:45:18 +02:00
Girish Ramakrishnan 87e7e9fa07 Add header to mailing list delete dialog 2018-04-11 19:04:26 -07:00
Girish Ramakrishnan 8643fbb65c Fix typo in mailbox delete dialog 2018-04-11 18:49:41 -07:00
Johannes Zellner 6a2846afeb Show full fqdn for apps in graphs 2018-04-11 19:19:05 +02:00
Johannes Zellner 7bfa23e2b1 Prevent email hash change listener to kick-in on view change 2018-04-11 12:16:44 +02:00
Girish Ramakrishnan bcd55972cd Remove many breaks
When email is disabled, there is just lots of empty space
2018-04-10 14:23:10 -07:00
Johannes Zellner d75e1d04b3 Do not use the same DOM element id twice 2018-04-10 18:05:35 +02:00
Johannes Zellner 02ef77398a Remove now unused apiOriginHostname variable from gulpfile 2018-04-10 18:00:18 +02:00
Johannes Zellner b0b19053a7 We now set the csp headers from nginx
This makes it easier to also support local development on non https
2018-04-10 17:59:46 +02:00
Johannes Zellner 5cc298555a Ensure assets have proper tag attributes and always source from / 2018-04-10 14:49:49 +02:00
Johannes Zellner f2a0dcca31 Cleanup some linter errors and usused classes 2018-04-10 14:49:20 +02:00
Johannes Zellner c274e60868 Remove unused noapp.html 2018-04-10 14:31:37 +02:00
Johannes Zellner bde6ef8797 Make new mailbox/list buttons large but give some space on the bottom 2018-04-10 14:11:16 +02:00
Johannes Zellner df15f63424 Remove unneccessary dashboard/ subfolder 2018-04-10 13:28:55 +02:00
Johannes Zellner 1bd4a0aa8e git ignore node_modules/ 2018-04-10 13:12:46 +02:00
Johannes Zellner f068ce4e85 Add package.json for gulp dependencies to build assets 2018-04-10 13:12:46 +02:00
Girish Ramakrishnan 20093c581c Make the button small instead 2018-04-09 15:37:05 -07:00
Girish Ramakrishnan 814d7bafa8 Remove the content-large to keep all views same size 2018-04-09 14:55:35 -07:00
Girish Ramakrishnan e07fac0335 Fix wording 2018-04-09 12:59:38 -07:00
Girish Ramakrishnan 39730c71ce Move buttons to top right 2018-04-09 12:29:00 -07:00
Johannes Zellner 8565130166 Allow deeplinking of domains into email view 2018-04-09 18:00:08 +02:00
Johannes Zellner 9acde7fe86 Add error feedback when mailbox name is already taken 2018-04-09 16:21:32 +02:00
Johannes Zellner 63e43e8d20 Move new mailinglist form to a dialog 2018-04-09 16:12:18 +02:00
Johannes Zellner 10a3af8e5e Ensure autofocus on dialog open 2018-04-09 15:58:25 +02:00
Johannes Zellner 14536febaf Move mailbox add form to dialog 2018-04-09 15:56:20 +02:00
Johannes Zellner 75b597418c Do not list app mailboxes 2018-04-09 15:45:54 +02:00
Johannes Zellner 435730470b Fix indentation 2018-04-09 15:08:05 +02:00
Johannes Zellner 689ddf6875 edit mailinglists with a dialog instead of inline 2018-04-09 15:01:12 +02:00
Johannes Zellner fa550f57b3 Style the alias input and fix some typos 2018-04-09 12:54:25 +02:00
Johannes Zellner b9b84b661a Remove unused css classes and make all mailbox lines edit on click 2018-04-09 12:49:25 +02:00
Johannes Zellner b7573f449f Remove mailbox inline editing 2018-04-09 12:45:00 +02:00
Johannes Zellner 69f6895bd6 Move mailbox edit to dialog 2018-04-09 12:42:14 +02:00
Johannes Zellner 72a1e0d5ca No need to console.error() if we show the error in the ui already 2018-04-06 16:58:46 +02:00
Johannes Zellner 4eb80eedc0 Avoid reflow of the ui when buttons are shown/hidden 2018-04-06 16:58:27 +02:00
Johannes Zellner 32e6931b46 Remove console.log()s 2018-04-06 16:56:19 +02:00
Johannes Zellner f60258ed71 Add mailbox add error handling 2018-04-06 16:51:57 +02:00
Johannes Zellner 32454ba64a Give the mailbox listing more space 2018-04-06 16:48:04 +02:00
Johannes Zellner de212f49c2 Fixup the mailinglist crud ui 2018-04-06 16:43:43 +02:00
Johannes Zellner c308bd90cb Ensure at least the index application assets are versioned for proper refresh 2018-04-06 00:11:08 +02:00
Johannes Zellner 593bde9d92 Ensure the main controller is aware of the newly setup subscription 2018-04-05 23:27:39 +02:00
Johannes Zellner a16bd7030a Fix the configure/select plan button in the settings 2018-04-05 23:27:14 +02:00
Johannes Zellner 4d248bce39 Give appstore login form more space on the bottom 2018-04-05 22:03:40 +02:00
Johannes Zellner e236264848 Fetch subscription directly after the cloudron was registered to update the ui 2018-04-05 21:49:15 +02:00
Johannes Zellner 20e9877fe9 Give mail view more horizontal space 2018-04-05 21:24:29 +02:00
Johannes Zellner b0c4021d17 Add mailbox delete ui 2018-04-05 21:22:07 +02:00
Johannes Zellner 01bfd84853 Fixup the mailbox edit logic 2018-04-05 21:15:02 +02:00
Johannes Zellner a0dbcc9bb3 Add ui bits to add mailboxes 2018-04-05 21:00:33 +02:00
Johannes Zellner bbe351161f Adjust mailbox wrapper functions 2018-04-05 14:02:56 +02:00
Girish Ramakrishnan 968f515679 Enter works (no need for comma) 2018-04-02 09:27:17 -07:00
Girish Ramakrishnan cbb5cb3702 your -> this 2018-04-02 09:24:24 -07:00
Girish Ramakrishnan 82ed1881ea Remove the mailboxes text 2018-04-02 09:23:29 -07:00
Johannes Zellner 4d13d309d3 Handle reserved alias errors 2018-04-02 15:01:01 +02:00
Johannes Zellner 75eae0d8ec Make asyncForEach available globally to reduce code duplication 2018-04-02 11:35:02 +02:00
Johannes Zellner c329541708 Remove mailinglist handling in users/groups view 2018-04-01 23:10:47 +02:00
Johannes Zellner 10b8e93713 Add mailinglist ui in the mail view 2018-04-01 22:42:21 +02:00
Johannes Zellner 963b1d60b5 Remove mailbox settings from user edit dialog 2018-04-01 20:53:49 +02:00
Johannes Zellner 158271de14 Add alias error reporting 2018-04-01 20:31:16 +02:00
Johannes Zellner baba63889d Add logic to set mailbox aliases 2018-04-01 19:12:06 +02:00
Johannes Zellner d52273a516 move mailbox and catchall sections above relay 2018-04-01 19:11:46 +02:00
Johannes Zellner 1b7556443f Add user table to manage mailboxes per domain
This does not yet handle the aliases
2018-03-30 18:34:00 +02:00
Johannes Zellner 9575a1158a Add user listing to mail view to manage per user mailboxes 2018-03-30 18:06:40 +02:00
Johannes Zellner 8ebcc2f8af Immediately check for new configuration after we check for updates 2018-03-30 15:29:45 +02:00
Johannes Zellner 8d6de76fa0 Show update button and dialog in settings view 2018-03-30 15:12:34 +02:00
Johannes Zellner 0ad813cc8d Move update button into notification instead of pill 2018-03-30 15:12:14 +02:00
Johannes Zellner 63ae9a90cf ensure we call the callback 2018-03-28 14:32:21 +02:00
Johannes Zellner 551912145e Refetch the subscription also on the main controller 2018-03-28 14:19:34 +02:00
Johannes Zellner 611f54c237 LICENSE EXPIRED looks quite harsh 2018-03-28 14:18:58 +02:00
Johannes Zellner 60c9f49b44 Show distinct setup billing button in settings 2018-03-28 12:41:20 +02:00
Johannes Zellner 9f66003755 Show trial state in settings view next to plan name 2018-03-28 12:29:37 +02:00
Johannes Zellner 862e1d94be Remove dead notification code 2018-03-28 12:26:53 +02:00
Johannes Zellner 8196f76847 Show different subscription bubbles based on the billing and subscription status 2018-03-28 12:26:24 +02:00
Johannes Zellner 09f1bb4653 Add alias error reporting 2018-03-27 20:19:17 +02:00
Johannes Zellner f626a1f0b7 Fixup all other async occurances 2018-03-27 19:40:20 +02:00
Johannes Zellner fd609d3e19 Fix poor man's async to break on and report errors 2018-03-27 19:38:09 +02:00
Johannes Zellner 977e83cc22 Do not send empty aliases 2018-03-27 18:34:41 +02:00
Johannes Zellner 59b3cabf7e parent.getSubscription() does not exist anymore 2018-03-26 15:22:39 +02:00
Johannes Zellner 4d85c36c16 Show app message if any on status label hover 2018-03-26 11:08:57 +02:00
Girish Ramakrishnan b762f80812 typo 2018-03-22 12:16:16 -07:00
Girish Ramakrishnan 4e0791eb22 Fix gulpfile to handle rename 2018-03-15 14:32:48 -07:00
Girish Ramakrishnan 392e6d1c98 fix gitignore 2018-03-15 14:25:08 -07:00
Girish Ramakrishnan 5a49a555ad we use the package files from the box repo 2018-03-15 14:24:21 -07:00
Girish Ramakrishnan d59cb63188 move files to dashboard/ 2018-03-15 14:23:51 -07:00
Johannes Zellner 1d0f87f408 Avoid reflow on settings page while fetching current subscription 2018-03-14 23:30:42 +01:00
Johannes Zellner a26264e8ce Remove usage of obsolete undecided subscription plan 2018-03-14 23:28:36 +01:00
Johannes Zellner ed716d7569 Sync the text what a subsription offers 2018-03-14 22:34:57 +01:00
Johannes Zellner f85fca1720 An update is actually a positive action 2018-03-14 20:38:17 +01:00
Johannes Zellner ed2539cbfc Change update dialog title and include version 2018-03-14 20:37:39 +01:00
Johannes Zellner 5405338d20 Remove redundant if condition 2018-03-14 20:10:48 +01:00
Johannes Zellner f8ad2fdc11 currentSubscription is not required anymore 2018-03-14 19:53:36 +01:00
Johannes Zellner a618f2b523 Make update button dependent on the sourceTarballUrl instead of the subscription type 2018-03-14 19:53:36 +01:00
Girish Ramakrishnan 8b5a88ba5e Remove break and just use some margin 2018-03-14 09:09:42 -07:00
Girish Ramakrishnan db9e3b44a1 Revert "Show all apps inactive and blurred in the background of the appstore login form"
This reverts commit b9d6c8f8bb.
2018-03-14 09:04:05 -07:00
Girish Ramakrishnan 634408d3a3 Fix the email instructions 2018-03-13 23:16:15 -07:00
Girish Ramakrishnan 529a668db3 Fix eventlog display 2018-03-13 22:03:25 -07:00
Girish Ramakrishnan c0f01da1cd Make it FREE TRIAL instead 2018-03-13 13:38:19 -07:00
Girish Ramakrishnan 4cbab59fdb Fix the setup billing link 2018-03-13 13:30:33 -07:00
Girish Ramakrishnan ec9c9fb0f5 Fix the subscription dialog text 2018-03-13 13:23:22 -07:00
Girish Ramakrishnan 286d634756 always require terms 2018-03-13 11:06:49 -07:00
Girish Ramakrishnan ca2457bfcb smallcase the Account 2018-03-13 11:00:41 -07:00
Girish Ramakrishnan 459cafdf56 Add description text during signup 2018-03-13 10:59:15 -07:00
Johannes Zellner b9d6c8f8bb Show all apps inactive and blurred in the background of the appstore login form 2018-03-13 10:50:03 +01:00
Johannes Zellner 2da019556b Improve and center appstore login form 2018-03-13 10:21:25 +01:00
Johannes Zellner cbd28bc12f Attempt to better position checkboxes 2018-03-13 10:16:31 +01:00
Johannes Zellner 4332f60cc4 Use the admin domain as the default when installing a new app 2018-03-13 09:38:55 +01:00
Girish Ramakrishnan 950179ee1c Just link to docs instead 2018-03-13 00:29:10 -07:00
Girish Ramakrishnan 803eb4760e Make text clearer 2018-03-13 00:24:33 -07:00
Girish Ramakrishnan 32a41e6c1c Clarify that users need a mailbox to access 2018-03-12 15:49:11 -07:00
Girish Ramakrishnan de195c461b make it text-info 2018-03-12 15:04:37 -07:00
Girish Ramakrishnan 5003a8ea4d Make it text-info for the underline to show 2018-03-12 14:17:58 -07:00
Girish Ramakrishnan caa41b0022 Fix the text to handle multi-domain email 2018-03-12 13:57:50 -07:00
Johannes Zellner c7151d2b8d Reduce newlines 2018-03-12 19:08:15 +01:00
Johannes Zellner 0929ae1a4c Remove unused app feedback dialog 2018-03-12 19:08:05 +01:00
Johannes Zellner 0c79c42c10 Use forum links for missing apps 2018-03-12 19:06:15 +01:00
Girish Ramakrishnan 028b24db03 Don't make the whole dialog red 2018-03-09 15:02:30 -08:00
Johannes Zellner bce3d3f664 Use fqdn instead of location for naked domain apps 2018-03-09 10:17:01 +01:00
Girish Ramakrishnan 828d6f6cc8 Show the provider and format for caas 2018-03-09 00:40:55 -08:00
Girish Ramakrishnan 0a026cc143 Display caas as Managed Cloudron 2018-03-09 00:37:20 -08:00
Girish Ramakrishnan 3bc9a87933 Fix display of caas domain 2018-03-09 00:29:00 -08:00
Girish Ramakrishnan 769f9adc9d Update mail domain when domain is updated 2018-03-08 18:06:50 -08:00
Johannes Zellner b5f53d921e Replace app-request link to point to the new forum 2018-03-08 21:46:16 +01:00
Girish Ramakrishnan 105e9e7825 Use the new app update pattern 2018-03-06 21:30:42 -08:00
Girish Ramakrishnan c8cf050156 Keep it alphabetical 2018-03-05 10:28:22 -08:00
Girish Ramakrishnan b7baafbbe6 actions -> events
also make it all past tense
2018-03-05 10:17:44 -08:00
Girish Ramakrishnan 85dde71ec3 fix display of undefined id
remove id display altogether, it's not very interesting to see it
2018-03-05 10:09:06 -08:00
Girish Ramakrishnan 2970b086a3 Updates -> App Updates 2018-03-05 09:39:03 -08:00
Johannes Zellner 5910709008 Use the correct model attribute for appId in feedback form 2018-03-05 17:10:00 +01:00
Johannes Zellner 2b6ce4f813 Reduce feedback form options and add ability to specify failing app 2018-03-05 12:54:09 +01:00
Johannes Zellner 451c697fb7 Show email as fallback when a user has no username yet 2018-03-05 12:14:20 +01:00
Johannes Zellner 09149318b1 Better format the multiselect element 2018-03-05 12:10:17 +01:00
Johannes Zellner d2d8eb9485 Allow to select multiple actions in the eventlog filter 2018-03-05 12:03:02 +01:00
Johannes Zellner 91265613a9 Prettify eventlog source display 2018-03-02 19:21:24 +01:00
Johannes Zellner 31c414bbe1 Use more readable datetime tooltip format in activity log 2018-03-02 18:58:49 +01:00
Johannes Zellner e2a3654ed7 Give the time more space in the activity log 2018-03-02 18:50:49 +01:00
Johannes Zellner 96d7283534 Do not alternate the background color of the activity log 2018-03-02 18:50:32 +01:00
Girish Ramakrishnan 256a7e322b Keep it all to two words 2018-03-02 09:12:53 -08:00
Johannes Zellner e5b78337ac Show more readable user event data 2018-03-02 13:42:24 +01:00
Johannes Zellner 67ba5aa1c5 fix indentation 2018-03-02 13:19:57 +01:00
Johannes Zellner 848a617f98 Make eventlog entries expandable to show raw event data 2018-03-02 10:50:05 +01:00
Johannes Zellner 1fc7efef0d Improve app related eventlog display 2018-03-02 10:49:46 +01:00
Girish Ramakrishnan 576f6eafbb Rename Chat to Forum 2018-03-01 13:40:10 -08:00
Girish Ramakrishnan 2caf73b5e3 Do not list mail domains and aliases if username is not available 2018-02-28 15:21:42 -08:00
Girish Ramakrishnan 56abb68e0c Link admin link to docs 2018-02-28 13:40:06 -08:00
Girish Ramakrishnan 7aaac5a48a reword email address on domains 2018-02-28 13:26:15 -08:00
Girish Ramakrishnan 8326587886 Give indication that the test is for the relay 2018-02-27 09:24:36 -08:00
Girish Ramakrishnan 466b3f4784 Make the user edit dialog say "Primary email" 2018-02-24 16:42:15 -08:00
Girish Ramakrishnan bccdf548a8 Fix typo making the MX records hidden 2018-02-23 17:04:38 -08:00
Girish Ramakrishnan fa4b1b3d5b Add note that user and group mailboxes must be enabled 2018-02-23 17:02:18 -08:00
Girish Ramakrishnan 9d47fd198f replace chat with forum 2018-02-23 15:53:23 -08:00
Girish Ramakrishnan 5966ee6800 replace terms link with license 2018-02-23 15:25:27 -08:00
Johannes Zellner 2d20e3c13d Scroll to top on category activation 2018-02-23 11:34:39 -08:00
Johannes Zellner 2172f8532d Rework the appstore category list 2018-02-23 11:34:27 -08:00
Johannes Zellner 9dc4318152 Reduce category item size 2018-02-23 11:34:04 -08:00
Johannes Zellner e1a92e7127 Make primary email labels explicit 2018-02-23 10:29:09 -08:00
Girish Ramakrishnan 767b31caa2 Display the pretty domain provider name in the table
This is especially needed to distinguish wildcard/manual.
2018-02-21 10:14:17 -08:00
Johannes Zellner c2232936e0 Replace chat with forum in the support page 2018-02-20 11:55:57 -08:00
Johannes Zellner 4f1bbfd9e3 Make it clear that support ssh button should be enabled only if we ask the user to do so 2018-02-20 11:27:12 -08:00
Johannes Zellner caf57e37dc Add eventlog groups for apps and users 2018-02-20 11:13:51 -08:00
Johannes Zellner 64b8e4ad6c Shorten app ids in eventlog 2018-02-19 01:56:12 -08:00
Johannes Zellner c9d3907124 Add missing whitespace 2018-02-19 01:56:12 -08:00
Girish Ramakrishnan bf6bea800b Add note that user/group mailboxes must be enabled 2018-02-18 12:04:37 -08:00
Johannes Zellner 26f1673d47 Show full fqdn on app grid item hover 2018-02-17 16:01:36 -08:00
Johannes Zellner 08153454a2 Show tooltips immediately for app actions to guide the user 2018-02-09 10:11:23 +01:00
Girish Ramakrishnan efc26ab587 Specify which domain mail should be enabled for 2018-02-08 19:08:00 -08:00
Girish Ramakrishnan e24e0a7e87 br was removed by mistake in 23bc267c46 2018-02-08 15:25:37 -08:00
Johannes Zellner 23bc267c46 Show full fqdn in apps grid for now instead of the domain on the top 2018-02-08 16:17:47 +01:00
Johannes Zellner 35cc592d61 Remove altDomain ui bits 2018-02-08 09:44:35 +01:00
Girish Ramakrishnan 512f6a1166 Remove obsolete action 2018-02-06 23:14:37 -08:00
Johannes Zellner 3160ffec3f The update schedule is only set for the apps now 2018-02-06 19:39:06 +01:00
Johannes Zellner c543d4517f Adjust to new autoupdate pattern rest apis 2018-02-06 19:25:06 +01:00
Girish Ramakrishnan d7334b991b Add DO SGP1 2018-02-05 11:06:40 -08:00
Girish Ramakrishnan 2b355f6ee4 typo 2018-02-03 18:23:49 -08:00
Girish Ramakrishnan cd6af57a6e Use SOS ng endpoint and v4
https://www.exoscale.ch/syslog/2018/01/09/object-storage/
2018-02-02 16:29:10 -08:00
Girish Ramakrishnan 674028ee77 This probably explains why people ask for support in issue tracker 2018-02-02 11:39:36 -08:00
Girish Ramakrishnan f51c2e7b61 pass env=dev as search param to set the tlsConfig 2018-01-31 21:02:12 -08:00
Johannes Zellner 182085d3f8 Remove the generation of the splash screen assets 2018-01-31 09:23:42 +01:00
Johannes Zellner b8e70f1160 Remove console.log() 2018-01-30 17:01:27 +01:00
Johannes Zellner c624c04e9d Hide alias and mailling list UI when no domain has email enabled 2018-01-30 16:33:27 +01:00
Johannes Zellner 666badfa38 Mailinglist remove route returns 204 2018-01-30 13:38:25 +01:00
Girish Ramakrishnan 21b9843083 Enable selector for now 2018-01-29 19:33:56 -08:00
Girish Ramakrishnan f3b53a22f5 Remove use of config.fqdn 2018-01-29 14:35:47 -08:00
Girish Ramakrishnan f850235b3a Remove ununsed setAdminCertificate 2018-01-26 20:46:58 -08:00
Girish Ramakrishnan f555cfddd0 Uncomment for testing 2018-01-26 11:53:26 -08:00
Johannes Zellner 1c9052f085 Rework the email domain and alias handling for user edits to fix various bugs 2018-01-26 15:39:35 +01:00
Johannes Zellner 6a450b7190 setAlias returns 202 on success 2018-01-26 15:39:02 +01:00
Johannes Zellner 75194525fb Remove unused function showBubble() 2018-01-26 15:04:18 +01:00
Johannes Zellner 9d57c81505 Add group edit ui to select mailing lists 2018-01-26 11:31:43 +01:00
Johannes Zellner 8af587b6d9 Add mailing list api wrappers 2018-01-26 11:31:19 +01:00
Girish Ramakrishnan c1cb2eda00 Add a hack for now to just add/remove mail domains as part of domains API 2018-01-25 13:40:45 -08:00
Girish Ramakrishnan 90c867da82 Fix the text 2018-01-25 10:08:29 -08:00
Johannes Zellner d97268dfa7 Give some busy indicator while email configs are fetching 2018-01-25 18:28:11 +01:00
Johannes Zellner c3ce6ef9f0 Add alias ui in user edit 2018-01-25 18:17:40 +01:00
Johannes Zellner eb3d6fe5f1 Use the new alias api in api wrapper 2018-01-25 18:16:47 +01:00
Johannes Zellner e39e7d4898 Adjust to the mailboxes route name change 2018-01-25 12:28:39 +01:00
Johannes Zellner b282375ac7 Also allow to change the fallbackEmail for users 2018-01-24 16:21:14 +01:00
Johannes Zellner 8deb28f8eb Add multiselect to enable mailboxes per user and domain 2018-01-24 16:20:56 +01:00
Johannes Zellner e91833dcf2 Add mailbox api wrappers to client.js 2018-01-24 16:20:21 +01:00
Girish Ramakrishnan ca9781c279 Fix display of domain 2018-01-23 20:43:49 -08:00
Girish Ramakrishnan 2d8412a629 Make API Key text clearer 2018-01-23 18:15:48 -08:00
Girish Ramakrishnan e4b253da22 test email now requires a domain 2018-01-23 16:10:09 -08:00
Girish Ramakrishnan 6edad6b986 remove enterprise checks 2018-01-23 11:55:36 -08:00
Johannes Zellner 7b7e94d3d6 Not sure what to do yet with alias ui in users settings, so disable to make the ui itself work 2018-01-23 17:09:47 +01:00
Johannes Zellner bc164281db Always show warning if an app requires email to be enabled for the selected domain
We can be smarter in the future and show this based on currently
selected domain from the dropdown
2018-01-23 16:51:53 +01:00
Johannes Zellner f94380b2dd Disable multidomain for domains and email view 2018-01-23 15:37:55 +01:00
Johannes Zellner a2241a4e47 Fix return code for email toggle to let the UI correctly react 2018-01-23 15:35:02 +01:00
Johannes Zellner 7e5afcfe6a Reenable and provision the catchall dropdown 2018-01-23 15:11:33 +01:00
Johannes Zellner 7c926cc051 Already update the ui while checking the mail status 2018-01-23 12:41:52 +01:00
Johannes Zellner 16f43e3eac Fixup the status check logic 2018-01-23 12:38:19 +01:00
Johannes Zellner 456577a166 First round of mail view refactoring 2018-01-23 12:30:35 +01:00
Johannes Zellner 7166604fc0 Temporarily disable toplevel email check notification 2018-01-23 12:30:16 +01:00
Johannes Zellner 0ae8d1ed4d Refactor the mail related api wrappers 2018-01-23 12:29:57 +01:00
Girish Ramakrishnan 4388aee70d add dot files 2018-01-22 17:44:51 -08:00
Girish Ramakrishnan 13e4ceff44 Initial commit 2018-01-22 13:01:38 -08:00
448 changed files with 23556 additions and 58299 deletions
+1 -5
View File
@@ -1,10 +1,6 @@
dist/
node_modules/
coverage/
webadmin/dist/
setup/splash/website/
installer/src/certs/server.key
# vim swap files
*.swp
-909
View File
@@ -1,909 +0,0 @@
[0.0.1]
- Hot Chocolate
[0.0.2]
- Hotfix appstore ui in webadim
[0.0.3]
- Tall Pike
[0.0.4]
- This will be 0.0.4 changes
[0.0.5]
- App install/configure route fixes
[0.0.6]
- Not sure what happenned here
[0.0.7]
- resetToken is now sent as part of create user
- Same as 0.0.7 which got released by mistake
[0.0.8]
- Manifest changes
[0.0.9]
- Fix app restore
- Fix backup issues
[0.0.10]
- Unknown orchestra
[0.0.11]
- Add ldap addon
[0.0.12]
- Support OAuth2 state
[0.0.13]
- Use docker image from cloudron repository
[0.0.14]
- Improve setup flow
[0.0.15]
- Improved Appstore view
[0.0.16]
- Improved Backup approach
[0.0.17]
- Upgrade testing
- App auto updates
- Usage graphs
[0.0.18]
- Rework backups and updates
[0.0.19]
- Graphite fixes
- Avatar and Cloudron name support
[0.0.20]
- Apptask fixes
- Chrome related fixes
[0.0.21]
- Increase nginx hostname size to 64
[0.0.22]
- Testing the e2e tests
[0.0.23]
- Better error status page
- Fix updater and backup progress reporting
- New avatar set
- Improved setup wizard
[0.0.24]
- Hotfix the ldap support
[0.0.25]
- Add support page
- Really fix ldap issues
[0.0.26]
- Add configurePath support
[0.0.27]
- Improved log collector
[0.0.28]
- Improve app feedback
- Restyle login page
[0.0.29]
- Update to ubuntu 15.04
[0.0.30]
- Move to docker 1.7
[0.0.31]
- WARNING: This update restarts your containers
- System processes are prioritized over apps
- Add ldap group support
[0.0.32]
- MySQL addon update
[0.0.33]
- Fix graphs
- Fix MySQL 5.6 memory usage
[0.0.34]
- Correctly mark apps pending for approval
[0.0.35]
- Fix ldap admin group username
[0.0.36]
- Fix restore without backup
- Optimize image deletion during updates
- Add memory accounting
- Restrict access to metadata from containers
[0.0.37]
- Prepare for Selfhosting 1. part
- Use userData instead of provisioning calls
[0.0.38]
- Account for Ext4 reserved block when partitioning disk
[0.0.39]
- Move subdomain management to the cloudron
[0.0.40]
- Add journal limit
- Fix reprovisioning on reboot
- Fix subdomain management during startup
[0.0.41]
- Finally bring things to a sane state
[0.0.42]
- Parallel apptask
[0.0.43]
- Move to systemd
[0.0.44]
- Fix apptask concurrency bug
[0.0.45]
- Retry subdomain registration
[0.0.46]
- Fix app update email notification
[0.0.47]
- Ensure box code quits within 5 seconds
[0.0.48]
- Styling fixes
- Improved session handling
[0.0.49]
- Fix app autoupdate logic
[0.0.50]
- Use domainmanagement via CaaS
[0.0.51]
- Fix memory management
[0.0.52]
- Restrict addons memory
- Get nofication about container OOMs
[0.0.53]
- Restrict addons memory
- Get notification about container OOMs
- Add retry to subdomain logic
[0.0.54]
- OAuth Proxy now uses internal port forwarding
[0.0.55]
- Setup cloudron timezone based on droplet region
[0.0.56]
- Use correct timezone in updater
[0.0.57]
- Fix systemd logging issues
[0.0.58]
- Ensure backups of failed apps are retained across archival cycles
[0.0.59]
- Installer API fixes
[0.0.60]
- Do full box backup on updates
[0.0.61]
- Track update notifications to inform admin only once
[0.0.62]
- Export bind dn and password from LDAP addon
[0.0.63]
- Fix creation of TXT records
[0.0.64]
- Stop apps in a retired cloudron
- Retry downloading application on failure
[0.0.65]
- Do not send crash mails for apps in development
[0.0.66]
- Readonly application and addon containers
[0.0.67]
- Fix email notifications
- Fix bug when restoring from certain backups
[0.0.68]
- Update graphite image
- Add simpleauth addon support
[0.0.69]
- Support newer manifest format
- Fix app listing rendering in chrome
- Fix redis backup across upgrades
[0.0.70]
- Retry app download on error
[0.0.71]
- Fix oauth and simple auth login
[0.0.72]
- Cleanup application volumes periodically
- New application logging design
[0.0.73]
- Update SSL certificate
[0.0.74]
- Support singleUser apps
[0.0.75]
- scheduler addon
[0.0.76]
- DNS Sync fixes
- Show warning to user when memory limit reached
[0.0.77]
- Do not set hostname in app containers
[0.0.78]
- Support custom domains
[0.0.79]
- Move SSH Port
[0.0.80]
- Use journalctl for container logs
[0.1.0]
- Wait for configuration changes before starting Cloudron
[0.1.1]
- Ensure dns config for all cloudrons
[0.1.2]
- Make email work again
- Add DKIM keys for custom domains
[0.1.3]
- Storage backend
[0.1.4]
- CaaS Backup configuration fix
[0.1.5]
- Use correct tokens for DNS backend
[0.1.6]
- Add hook to determine the api server of the box
- Fix crash notification
[0.2.0]
- New cloudron exec implementation
[0.2.1]
- Update to node 4.1.1
- Fix certification installation with custom domains
[0.2.2]
- Better debug output
- Retry more times if docker registry goes down
[0.3.0]
- Update SSH keys
- Allow bigger manifest files
[0.4.0]
- Update to docker 1.9.0
[0.4.1]
- Fix scheduler crash
- Crucial OAuth fixes
[0.4.2]
- Fix crash when reporting backup error
- Allow larger manifests
[0.4.3]
- Fix cloudron exec
[0.4.4]
- Initial Lets Encrypt integration
[0.4.5]
- Fixup nginx configuration to allow dynamic certificates
[0.4.6]
- LetsEncrypt integration for custom domains
- Rate limit crash emails
[0.5.0]
- Enable staging Lets Encrypt Integration
[0.5.1]
- Display error dialog for app installation errors
- Enable prod Lets Encrypt Integration
- Handle apptask crashes correctly
[0.5.2]
- Fix apphealthtask crash
- Use cgroup fs driver instead of systemd cgroup driver in docker
[0.5.3]
- Changes for e2e testing
[0.5.4]
- Fix bug in LE server selection
[0.5.5]
- Scheduler redesign
- Fix journalctl logging
[0.5.6]
- Prepare for selfhosting option
[0.5.7]
- Move app images off the btrfs subvolume
[0.6.0]
- Consolidate code repositories
[0.6.1]
- Use no-reply as email from address for apps in naked domains
- Update Lets Encrypt account with owner email when available
- Fix email templates to indicate auto update
- Add notification UI
[0.6.2]
- Fix `cloudron exec` container to have same namespaces as app
- Add developmentMode to manifest
[0.6.3]
- Make sending invite for new users optional
[0.6.4]
- Add support for display names
- Send invite links to admins for user setup
- Enforce stronger passwords
[0.6.5]
- Finalize stronger password requirement
[0.7.0]
- Upgrade to 15.10
- Do not remove docker images when in use by another container
- Fix sporadic error when reconfiguring apps
- Handle journald crashes gracefully
[0.7.1]
- Allow admins to edit users
- Fix graphs
- Support more LDAP cases
- Allow appstore deep linking
[0.7.2]
- Fix 5xx errors when password does not meet requirements
- Improved box update management using prereleases
- Less aggressive disk space checks
[0.8.0]
- MySQL addon : multiple database support
[0.8.1]
- Set Host HTTP header when querying healthCheckPath
- Show application Changelog in app update emails
[0.9.0]
- Fix bug in multdb mysql addon backup
- Add initial user group support
- Improved app memory limit handling
[0.9.1]
- Introduce per app group access control
[0.9.2]
- Fix bug where reconfiguring apps would trigger memory limit warning
- Allow more apps to be installed in bigger sized cloudrons
- Allow user to override memory limit warning and install anyway
[0.9.3]
- Admin flag is handled outside of groups
- User interface fixes for groups
- Allow to set access restrictions on app installation
[0.10.0]
- Upgrade to docker 1.10.2
- Fix MySQL addon to handle heavier loads
- Allow listing and download of backups (using the CLI tool)
- Ubuntu security updates till 8th March 2016 (http://www.ubuntu.com/usn)
[0.10.1]
- Fix Let's Encrypt certificate renewal
[0.10.2]
- Apps can now bind with username or email with LDAP
- Disallow updating an app with mismatching manifest id
- Use admin domain instead of naked domain in the SPF record
- Download Lets Encrypt intermediate cert
[0.10.3]
- Store the backup config for each backup. This will allow using multiple buckets/providers for backups simultaneously.
- Fix SPF record check
[0.10.4]
- Fix restore for droplets in EU region
[0.11.0]
- Store backups in the same region as the Cloudron
- Fix PCRE security issue (http://www.ubuntu.com/usn/usn-2943-1/)
[0.11.1]
- Improve the backup logic
[0.11.2]
- Allow users to choose a username on first sign up
- Fix app graphs
[0.12.0]
- Fix upload of large backups
- Postgres addon whitelists pg_trgm and hstore extensions
- Suppress boring update emails from patch releases
- Setup bounce alerts for emails
- Query admin's name in activation wizard
- Admin emails are now delivered as no-reply
- Fix crash when user attempts to set a duplicate email
- Improved mongodb crash recovery
[0.12.1]
- Fix crash when backing up apps
[0.12.2]
- Improved error handling for addons
[0.12.3]
- LDAP: Do not set sn attribute when user has no surname
[0.12.4]
- Install app only after platform is ready
[0.12.5]
- Get alerts for app task failures
- Fix update issue when one or more apps are in failed state
[0.12.6]
- Allow setting an alternate external domain for apps
[0.12.7]
- Fix changing password
[0.13.0]
- Upgrade to ubuntu 16.04
- Add event log
[0.13.1]
- Make activity log viewable to admins
- Fix geoip lookup
[0.13.2]
- Fix crash in app auto updater
- Fix crash with empty timezone
[0.13.3]
- Enable auth in email addon
- Add search for activity log
- Add tutorial for first time users
[0.13.4]
- Fix mail addon restart issue
[0.14.0]
- You have mail :-)
[0.14.1]
- 2-character usernames are now allowed
- Make cloudron CLI push/pull more robust
[0.14.2]
- Update mail addon
[0.15.0]
- [REST API](https://cloudron.io/references/api.html) is now in public beta
- Enable Developer mode by default for new Cloudrons
- Reverse proxy fixes for apps exposing a WebDav server
- Allow admins to optionally set the username and displayName on user creation
- Fix app autoupdate logic to detect if one or more in-use port bindings was removed
[0.15.1]
- Fix mail connectivity from IPv6 clients
- Add API token management UI
- Improved UI to enter email aliases
[0.15.2]
- Allow restoring apps from any previous backup
[0.15.3]
- Show installation progress in a tooltip
[0.16.0]
- Allow apps to be configured in configuring state
- Improved platform architecture that allows incremental infrastructure updates
- Implement app clone
[0.16.1]
- Fix UI layout issue in tokens page
- Resume app tasks only when configured and platform ready
- Allow errored apps to be reconfigured
[0.16.2]
- Fix assert when backing up apps in errored state
- Fix bug where multiple redis installations caused an error
[0.16.3]
- Timeout in 10mins if app restore fails because of external domain CNAME setup
[0.16.4]
- Setup email aliases to only alias names for the Cloudron domain
[0.16.5]
- Allow sending email with alias as the From
[0.16.6]
- Add plan migration interface
- Initial EC2 support
[0.17.0]
- Public beta release of Cloudron Mail Server
- Add new DNS & Certs UI that enables easy migration to a custom domain
- Allow sending and receiving email from alias subaddresses
- Fix installation issue with some apps on the naked domain
[0.17.1]
- Preliminary user impersonation support
- Fix crash in mail container when generating bounces
[0.17.2]
- Add config option to embed apps in other sites
[0.17.3]
- Incremental infrastructure update logic
- Keep eventlogs only for a week
- Throttle OOM mails
[0.17.4]
- Add warning for users moving to custom domains
- Out of disk space and certificate renewal mails are now sent to cloudron owner for selfhosters
- Fix a bug where selfhosted Cloudrons do not start because of a MySQL error
- Implement new app versioning & update scheme
[0.17.5]
- Fix migration interface issue
- Allow self hosted Cloudron to login to the Cloudron Store
- Send mail to self hosted Cloudron admins about OOM and App died errors
- Fix bug where box update emails are sent repeatedly
[0.18.0]
- Fix app bundle installation
- Fix RBL lookup in mail server
- Add spam filter for email
[0.19.0]
* New base image 0.19.0
* Upgrade PostgreSQL and MySQL
[0.19.1]
* Make email optional (settings -> enable/disable mail)
* Make PostgresSQL behave better in low memory cloudrons
* Add demo mode check
* Fix plan listing
[0.20.0]
* Fix bug where crash reports where not being sent to support@cloudron.io (#29)
* Do not overwrite existing DNS records during app installation (#27)
* Add UI to configure app's memory limit (#18)
* Fix OAuth proxy support (#6)
[0.20.1]
* Fix bug where oauth proxy was installed for apps with customAuth
[0.20.2]
* Fix memory limit slider to start from the minimum memory (#43)
* Save user certs separately from automatic certs (#44)
* Fix access control display for email apps (#45)
[0.20.3]
* Make DigitalOcean selfhosting independent
[0.21.0]
* Delivery of email to aliases is now case insensitive (#35)
* Mailing list support via Groups (#15)
* Fix issue where non-admin users could not update their profile
[0.21.1]
* Fix app clone error (mailbox was not allocated)
* Do not allow "-" in group names
[0.22.0]
* Rebuild server instances instead of recreating
[0.50.0]
* Add UI to configure backup location
* Add DNS backend to make it easy to run on any server with SSH access
* Update wildcard certificate
* Fix crash in mail container with SPF plugin
* Fix postgresql addon to restore correctly
* Periodically cleanup file system backups
* Improve invitation emails
* Fix bug where mailbox name was generated incorrectly for nake domain (#81)
[0.60.0]
* Implement new approach to selfhosting. `cloudron machine create` is now deprecated.
Please see the [selfhosting guide](https://cloudron.io/references/selfhosting.html)
for more details
* Send email to admins if backup fails
* Add UI to set digitalocean as DNS provider
[0.60.1]
* Apply less strict hostname checking for email
* Fix bug in Cloudron plan listing
* Improved storage provider interface
[0.70.0]
* Remove standalone installer daemon
[0.70.1]
* Add additional platform healthcheck
[0.80.0]
* Add optional SSO for apps
* Improve app status page
* Several webinterface improvements
[0.80.1]
* Improved DNS handling
* Better error messages in UI
[0.90.0]
* Remove customAuth support
* Support non AWS S3 object storage
* Settings UI improvements
[0.91.0]
* Support installing Cloudron on intranet and VirtualBox
* Fix bug where relocating an app did not free the old location
* Allow Email server to be enabled with wildcard DNS
[0.92.0]
* Backup encryption key is now optional
* Fix bug where DNS mail record warning was shown by mistake
* Make cloudron-setup finish with `manual` DNS provider
[0.92.1]
* Remove DO specific grub cmd line
* Fix License text
[0.93.0]
* Smoother upgrades
[0.94.0]
* Cloudron domain can now be set after installation
* Backups are now organized by directory
* Document upgrading from Filesystem backend
* Send certificate renewal errors, OOM errors to cloudron admins
* Email bounce alerts are sent to the Cloudron owner
[0.94.1]
* Suppress upgrade emails
* Enable unattended upgrades
* Standardize on using devicemapper for docker storage backend
* Show detailed backup progress
* Fix DNSBL issue in mail container
* Fix issue where bounce emails were not sent to aliases
* Remove tutorial
* Restart mail container on certificate change
[0.97.0]
* Fix missing app icon issue
* Fix issue where box sends out crash reports incessantly
* (API) Allow memory limit to be set to -1 (unlimited)
* (API) Move developmentMode flag from manifest to apps route
[0.98.0]
* Send stat on whether email is enabled
* Fix bug where heartbeat was sent for self-hosted Cloudrons
* Make Cloudron function even when disk is full
* Fix thunderbird connection issue
* Send more detailed logs for backup failures
* Restart nginx if it crashed automatically
* Support all DNS providers for managed Cloudrons
* Add granular configuration for auto-updates
[0.99.0]
* Fix bug where ports <= 1023 were not reserved
* Cleanup graphs UI
* Polish webadmin UI
* Fix bug where hard disk size was detected incorrectly
[0.99.1]
* Fix bug with duplicate nginx configs
[0.100.0]
* Improve DNS notifications for email
* Do not enable HSTS for subdomains
[0.100.1]
* Fix crash when fetching mail records
* Fix crash in LDAP server when username and displayName are empty
[0.101.0]
* New base image 0.10.0
* Better error handling of unpurchase errors
* Validate that cloudron domain name is a subdomain of public suffic list
* Add canada and london to S3 backup regions
* Bundle Font Awesome as part of webadmin
* Fix crash in custom certiicate validation
* Get A+ rating in SSL Check
* More robust detection and injection of SPF record
* Add azure, lightsail, linode, ovh, vultr to provider list
[0.102.0]
* Fix issue where SPF record check was only done 5 times (updated 'async')
* Make auto-generated self-signed cert load quickly on Firefox
* Ensure we download docker images and have an app data volume on app re-configure
* Improve certificate renewal erorr message
* Fix disk usage graph
* Show Repair UI for errored apps
[0.102.1]
* Add terms link when signing up for Cloudron.io account
* Fix issue where Cloudrons with many apps (> 35) were unable to backup
* Improve wording of DNS Setup
[0.103.0]
* Do not send crash logs and other notifications to support@cloudron.io for self-hosted instances
* Make auto-generated self-signed cert load quickly on Firefox (take 2)
[0.104.0]
* (mail) Fix crash when sending mails to groups with just 1 user
* (ldap) Add isadmin attribute to better map users in apps
* (ldap) Hide users which have not yet set a username in ldap searches
* (core) Add SSH authorized_keys management
* (core) Add additional security related headers to the nginx reverse proxy
* (ui) Add remote SSH support option
* (ui) Fix eventlog display
* (ui) Fix CNAME setup information
[0.105.0]
* Always show email related checks
* Show outbound SMTP port 25 status
* Hide remote feature for normal users
* Only list users via ldap searches who have access to the app
* Fix installation issue on servers with a differente locale set
[0.105.1]
* Fix crash when setupToken is not provided in activate API
* Add inline Docker GPG key
* Re-download icon when repairing app
* Fix issue where pre-installed apps were not installed correctly
* Fix issue where new cloudrons could not be activated
[0.106.0]
* (mail) Fix email forwarding to external domains
* (mail) Set maximum email size to 25MB
* Remove SimpleAuth addon
[0.107.0]
* Support CSP for webinterface and OAuth views
* (mail) Fix issue where Cloudron is only used to send emails
[0.108.0]
* Redirect to /setupdns.html when restoring
* Fix setting custom avatar
* Do not allocate more than 4GB swap
* Generate real passwords for sendmail/recvmail addons
* Rate limit all authentication routes to prevent password brute force
* Generate 128 byte password for MySQL multi-db addon
[0.109.0]
* Add Referrer-policy
* Add tooltip for admin email field explaining it is local & private
* Verify AMI instance id during DNS setup instead of admin account setup
* Split platform and app data folders and get rid of btrfs volumes
[0.110.0]
* Fix disk usage graphs
* Add --data-dir to cloudron-setup that allows customizing data location
* Add UI to restore from any app backup
* (mysql) Use utf8mb4 encoding for databases and backups
* Allow installing a new app from a backup
* Fix download of large files (> 1GB)
* Fix app backup regression
[0.120.0]
* Update Docker to 17.03.1-ce
* Rework backup backend logic
* Add UI to download logs
* Fix crash when checking mail dns settings
* Allow backup retention duration to be configured
* Add minio backend for backups
* Fix issue where Cloudron's with errored apps won't backup when using fs backend
* Fix DNS check issue where PTR records was read from hosts file
[0.120.1]
* Fix managed Cloudron backup cleanup
[0.130.0]
* Use Cloudron DNS server only for containers created by Cloudron
* Make Cloudron always start even if DNS credentials are invalid
* Show warning if DNS configuration is not valid
* Drop the '.enc' extension for non-encrypted backups
* Do not encrypt backups when the backup key is empty
* Do a multipart S3 download for slow internet connections
* Support naked domains as external location
[0.130.1]
* Fix app configure dialog regression
[0.130.2]
* Fix app configure dialog regression and dns setup screen
[0.130.3]
* Show error message if setup fails due to reserved username
* (security) Do not print password in the logs in the configure route
* Fix restore of unencrypted backups
* Fix bug where FS backups have incorrect extension for unencrypted backups
[0.140.0]
* HTTP2 support
* Condense the dns checks in the settings view
* Document new app store submission guidelines
[0.150.0]
* Disable dnsmasq on OVH
* Scale redis memory based on the app's memory limit
* (security) Do not print the ssl cert in debug logs
* Add noop storage backend to temporarily disable backups
* Replace native-dns module with dig to prevent spurious crashes
* Cleanup unfinished and errored backups
* Set a timelimit of 4 hours for backup to finish
[0.160.0]
* Fix disk graphs when using device mapper
* Prevent email view from flickering
* Prepare for 1.0
[1.0.0]
* Make selfhosting great again
[1.0.1]
* Notification improvements
[1.0.2]
* Notification improvements
[1.1.0]
* Add support for email catch-all
* Support Cloudrons on subdomains
[1.1.1]
* Notification improvements
[1.1.2]
* Notification improvements
[1.1.3]
* Notification improvements
+34 -660
View File
@@ -1,661 +1,35 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
The Cloudron Subscription license
Copyright (c) 2018 Cloudron UG
With regard to the Cloudron Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Cloudron Subscription Terms of Service, available
at https://cloudron.io/legal/terms.html (the “Subscription Terms”), or other
agreement governing the use of the Software, as agreed by you and Cloudron,
and otherwise have a valid Cloudron Subscription. Subject to the foregoing sentence,
you are free to modify this Software and publish patches to the Software. You agree
that Subscription and/or its licensors (as applicable) retain all right, title and
interest in and to all such modifications and/or patches, and all such modifications
and/or patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid Cloudron subscription. Notwithstanding the foregoing, you may copy
and modify the Software for development and testing purposes, without requiring a
subscription. You agree that Cloudron and/or its licensors (as applicable) retain
all right, title and interest in and to all such modifications. You are not
granted any other rights beyond what is expressly stated herein. Subject to the
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
For all third party components incorporated into the Cloudron Software, those
components are licensed under the original license provided by the owner of the
applicable component.
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
box
Copyright (C) 2016 Cloudron UG
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
+6 -25
View File
@@ -1,4 +1,4 @@
# Cloudron
# Cloudron Dashboard
[Cloudron](https://cloudron.io) is the best way to run apps on your server.
@@ -9,10 +9,6 @@ a complex task.
We are building the ultimate platform for self-hosting web apps. The Cloudron allows
anyone to effortlessly host web applications on their server on their own terms.
Support us on
[![Flattr Cloudron](https://button.flattr.com/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=cloudron&url=https://cloudron.io&title=Cloudron&tags=opensource&category=software)
or [pay us a coffee](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8982CKNM46D8U)
## Features
* Single click install for apps. Check out the [App Store](https://cloudron.io/appstore.html).
@@ -33,9 +29,9 @@ or [pay us a coffee](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_
* Trivially migrate to another server keeping your apps and data (for example, switch your
infrastructure provider or move to a bigger server).
* Comprehensive [REST API](https://cloudron.io/references/api.html).
* Comprehensive [REST API](https://cloudron.io/developer/api/).
* [CLI](https://git.cloudron.io/cloudron/cloudron-cli) to configure apps.
* [CLI](https://cloudron.io/documentation/cli/) to configure apps.
* Alerts, audit logs, graphs, dns management ... and much more
@@ -49,35 +45,20 @@ You can install the Cloudron platform on your own server or get a managed server
from cloudron.io. In either case, the Cloudron platform will keep your server and
apps up-to-date and secure.
* [Selfhosting](https://cloudron.io/references/selfhosting.html) - [Pricing](https://cloudron.io/pricing.html)
* [Selfhosting](https://cloudron.io/documentation/installation/) - [Pricing](https://cloudron.io/pricing.html)
* [Managed Hosting](https://cloudron.io/managed.html)
The wiki has instructions on how you can install and update the Cloudron and the
apps from source.
## Documentation
* [User manual](https://cloudron.io/references/usermanual.html)
* [Developer docs](https://cloudron.io/documentation.html)
* [Architecture](https://cloudron.io/references/architecture.html)
* [Documentation](https://cloudron.io/documentation/)
## Related repos
The [base image repo](https://git.cloudron.io/cloudron/docker-base-image) is the parent image of all
the containers in the Cloudron.
The [graphite repo](https://git.cloudron.io/cloudron/docker-graphite) contains the graphite code
that collects metrics for graphs.
The addons are located in separate repositories
* [Redis](https://git.cloudron.io/cloudron/redis-addon)
* [Postgresql](https://git.cloudron.io/cloudron/postgresql-addon)
* [MySQL](https://git.cloudron.io/cloudron/mysql-addon)
* [Mongodb](https://git.cloudron.io/cloudron/mongodb-addon)
* [Mail](https://git.cloudron.io/cloudron/mail-addon)
## Community
* [Chat](https://chat.cloudron.io/)
* [Forum](https://forum.cloudron.io/)
* [Support](mailto:support@cloudron.io)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-193
View File
@@ -1,193 +0,0 @@
#!/bin/bash
set -eu -o pipefail
assertNotEmpty() {
: "${!1:? "$1 is not set."}"
}
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)"
export JSON="${SOURCE_DIR}/node_modules/.bin/json"
INSTANCE_TYPE="t2.micro"
BLOCK_DEVICE="DeviceName=/dev/sda1,Ebs={VolumeSize=20,DeleteOnTermination=true,VolumeType=gp2}"
SSH_KEY_NAME="id_rsa_yellowtent"
revision=$(git rev-parse HEAD)
ami_name=""
server_id=""
server_ip=""
destroy_server="yes"
deploy_env="prod"
image_id=""
args=$(getopt -o "" -l "revision:,name:,no-destroy,env:,region:" -n "$0" -- "$@")
eval set -- "${args}"
while true; do
case "$1" in
--env) deploy_env="$2"; shift 2;;
--revision) revision="$2"; shift 2;;
--name) ami_name="$2"; shift 2;;
--no-destroy) destroy_server="no"; shift 2;;
--region)
case "$2" in
"us-east-1")
image_id="ami-6edd3078"
security_group="sg-a5e17fd9"
subnet_id="subnet-b8fbc0f1"
;;
"eu-central-1")
image_id="ami-5aee2235"
security_group="sg-19f5a770" # everything open on eu-central-1
subnet_id=""
;;
*)
echo "Unknown aws region $2"
exit 1
;;
esac
export AWS_DEFAULT_REGION="$2" # used by the aws cli tool
shift 2
;;
--) break;;
*) echo "Unknown option $1"; exit 1;;
esac
done
# TODO fix this
export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY}"
export AWS_SECRET_ACCESS_KEY="${AWS_ACCESS_SECRET}"
readonly ssh_keys="${HOME}/.ssh/id_rsa_yellowtent"
readonly SSH="ssh -o IdentitiesOnly=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ${ssh_keys}"
if [[ ! -f "${ssh_keys}" ]]; then
echo "caas ssh key is missing at ${ssh_keys} (pick it up from secrets repo)"
exit 1
fi
if [[ -z "${image_id}" ]]; then
echo "--region is required (us-east-1 or eu-central-1)"
exit 1
fi
function get_pretty_revision() {
local git_rev="$1"
local sha1=$(git rev-parse --short "${git_rev}" 2>/dev/null)
echo "${sha1}"
}
function wait_for_ssh() {
echo "=> Waiting for ssh connection"
while true; do
echo -n "."
if $SSH ubuntu@${server_ip} echo "hello"; then
echo ""
break
fi
sleep 5
done
}
now=$(date "+%Y-%m-%d-%H%M%S")
pretty_revision=$(get_pretty_revision "${revision}")
if [[ -z "${ami_name}" ]]; then
ami_name="box-${deploy_env}-${pretty_revision}-${now}"
fi
echo "=> Create EC2 instance"
id=$(aws ec2 run-instances --image-id "${image_id}" --instance-type "${INSTANCE_TYPE}" --security-group-ids "${security_group}" --block-device-mappings "${BLOCK_DEVICE}" --key-name "${SSH_KEY_NAME}" --subnet-id "${subnet_id}" --associate-public-ip-address \
| $JSON Instances \
| $JSON 0.InstanceId)
[[ -z "$id" ]] && exit 1
echo "Instance created ID $id"
echo "=> Waiting for instance to get a public IP"
while true; do
server_ip=$(aws ec2 describe-instances --instance-ids ${id} \
| $JSON Reservations.0.Instances \
| $JSON 0.PublicIpAddress)
if [[ ! -z "${server_ip}" ]]; then
echo ""
break
fi
echo -n "."
sleep 1
done
echo "Got public IP ${server_ip}"
wait_for_ssh
echo "=> Fetching cloudron-setup"
while true; do
if $SSH ubuntu@${server_ip} wget "https://cloudron.io/cloudron-setup" -O "cloudron-setup"; then
echo ""
break
fi
echo -n "."
sleep 5
done
echo "=> Running cloudron-setup"
$SSH ubuntu@${server_ip} sudo /bin/bash "cloudron-setup" --env "${deploy_env}" --provider "ami" --skip-reboot
wait_for_ssh
echo "=> Removing ssh key"
$SSH ubuntu@${server_ip} sudo rm /home/ubuntu/.ssh/authorized_keys /root/.ssh/authorized_keys
echo "=> Creating AMI"
image_id=$(aws ec2 create-image --instance-id "${id}" --name "${ami_name}" | $JSON ImageId)
[[ -z "$id" ]] && exit 1
echo "Creating AMI with Id ${image_id}"
echo "=> Waiting for AMI to be created"
while true; do
state=$(aws ec2 describe-images --image-ids ${image_id} \
| $JSON Images \
| $JSON 0.State)
if [[ "${state}" == "available" ]]; then
echo ""
break
fi
echo -n "."
sleep 5
done
if [[ "${destroy_server}" == "yes" ]]; then
echo "=> Deleting EC2 instance"
while true; do
state=$(aws ec2 terminate-instances --instance-id "${id}" \
| $JSON TerminatingInstances \
| $JSON 0.CurrentState.Name)
if [[ "${state}" == "shutting-down" ]]; then
echo ""
break
fi
echo -n "."
sleep 5
done
fi
echo ""
echo "Done."
echo ""
echo "New AMI is: ${image_id}"
echo ""
-179
View File
@@ -1,179 +0,0 @@
#!/bin/bash
set -eu -o pipefail
assertNotEmpty() {
: "${!1:? "$1 is not set."}"
}
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)"
export JSON="${SOURCE_DIR}/node_modules/.bin/json"
revision=$(git rev-parse HEAD)
box_name=""
server_id=""
server_ip=""
destroy_server="yes"
deploy_env="dev"
# Only GNU getopt supports long options. OS X comes bundled with the BSD getopt
# brew install gnu-getopt to get the GNU getopt on OS X
[[ $(uname -s) == "Darwin" ]] && GNU_GETOPT="/usr/local/opt/gnu-getopt/bin/getopt" || GNU_GETOPT="getopt"
readonly GNU_GETOPT
args=$(${GNU_GETOPT} -o "" -l "revision:,regions:,size:,name:,no-destroy,env:" -n "$0" -- "$@")
eval set -- "${args}"
while true; do
case "$1" in
--env) deploy_env="$2"; shift 2;;
--revision) revision="$2"; shift 2;;
--name) box_name="$2"; destroy_server="no"; shift 2;;
--no-destroy) destroy_server="no"; shift 2;;
--) break;;
*) echo "Unknown option $1"; exit 1;;
esac
done
echo "Creating digitalocean image"
if [[ "${deploy_env}" == "staging" ]]; then
assertNotEmpty DIGITAL_OCEAN_TOKEN_STAGING
export DIGITAL_OCEAN_TOKEN="${DIGITAL_OCEAN_TOKEN_STAGING}"
elif [[ "${deploy_env}" == "dev" ]]; then
assertNotEmpty DIGITAL_OCEAN_TOKEN_DEV
export DIGITAL_OCEAN_TOKEN="${DIGITAL_OCEAN_TOKEN_DEV}"
elif [[ "${deploy_env}" == "prod" ]]; then
assertNotEmpty DIGITAL_OCEAN_TOKEN_PROD
export DIGITAL_OCEAN_TOKEN="${DIGITAL_OCEAN_TOKEN_PROD}"
else
echo "No such env ${deploy_env}."
exit 1
fi
vps="/bin/bash ${SCRIPT_DIR}/digitalocean.sh"
readonly ssh_keys="${HOME}/.ssh/id_rsa_caas_${deploy_env}"
readonly scp202="scp -P 202 -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ${ssh_keys}"
readonly scp22="scp -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ${ssh_keys}"
readonly ssh202="ssh -p 202 -o IdentitiesOnly=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ${ssh_keys}"
readonly ssh22="ssh -o IdentitiesOnly=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ${ssh_keys}"
if [[ ! -f "${ssh_keys}" ]]; then
echo "caas ssh key is missing at ${ssh_keys} (pick it up from secrets repo)"
exit 1
fi
function get_pretty_revision() {
local git_rev="$1"
local sha1=$(git rev-parse --short "${git_rev}" 2>/dev/null)
echo "${sha1}"
}
now=$(date "+%Y-%m-%d-%H%M%S")
pretty_revision=$(get_pretty_revision "${revision}")
if [[ -z "${box_name}" ]]; then
# if you change this, change the regexp is appstore/janitor.js
box_name="box-${deploy_env}-${pretty_revision}-${now}" # remove slashes
# create a new server if no name given
if ! caas_ssh_key_id=$($vps get_ssh_key_id "caas"); then
echo "Could not query caas ssh key"
exit 1
fi
echo "Detected caas ssh key id: ${caas_ssh_key_id}"
echo "Creating Server with name [${box_name}]"
if ! server_id=$($vps create ${caas_ssh_key_id} ${box_name}); then
echo "Failed to create server"
exit 1
fi
echo "Created server with id: ${server_id}"
# If we run scripts overenthusiastically without the wait, setup script randomly fails
echo -n "Waiting 120 seconds for server creation"
for i in $(seq 1 24); do
echo -n "."
sleep 5
done
echo ""
else
if ! server_id=$($vps get_id "${box_name}"); then
echo "Could not determine id from name"
exit 1
fi
echo "Reusing server with id: ${server_id}"
$vps power_on "${server_id}"
fi
# Query until we get an IP
while true; do
echo "Trying to get the server IP"
if server_ip=$($vps get_ip "${server_id}"); then
echo "Server IP : [${server_ip}]"
break
fi
echo "Timedout, trying again in 10 seconds"
sleep 10
done
while true; do
echo "Trying to copy init script to server"
if $scp22 "${SCRIPT_DIR}/initializeBaseUbuntuImage.sh" root@${server_ip}:.; then
break
fi
echo "Timedout, trying again in 30 seconds"
sleep 30
done
echo "Copying infra_version.js"
$scp22 "${SCRIPT_DIR}/../src/infra_version.js" root@${server_ip}:.
echo "Copying box source"
cd "${SOURCE_DIR}"
git archive --format=tar HEAD | $ssh22 "root@${server_ip}" "cat - > /tmp/box.tar.gz"
echo "Executing init script"
if ! $ssh22 "root@${server_ip}" "/bin/bash /root/initializeBaseUbuntuImage.sh caas"; then
echo "Init script failed"
exit 1
fi
echo "Shutting down server with id : ${server_id}"
$ssh22 "root@${server_ip}" "shutdown -f now" || true # shutdown sometimes terminates ssh connection immediately making this command fail
# wait 10 secs for actual shutdown
echo "Waiting for 10 seconds for server to shutdown"
sleep 30
echo "Powering off server"
if ! $vps power_off "${server_id}"; then
echo "Could not power off server"
exit 1
fi
snapshot_name="box-${deploy_env}-${pretty_revision}-${now}"
echo "Snapshotting as ${snapshot_name}"
if ! image_id=$($vps snapshot "${server_id}" "${snapshot_name}"); then
echo "Could not snapshot and get image id"
exit 1
fi
if [[ "${destroy_server}" == "yes" ]]; then
echo "Destroying server"
if ! $vps destroy "${server_id}"; then
echo "Could not destroy server"
exit 1
fi
else
echo "Skipping server destroy"
fi
echo "Transferring image ${image_id} to other regions"
$vps transfer_image_to_all_regions "${image_id}"
echo "Done."
-261
View File
@@ -1,261 +0,0 @@
#!/bin/bash
if [[ -z "${DIGITAL_OCEAN_TOKEN}" ]]; then
echo "Script requires DIGITAL_OCEAN_TOKEN env to be set"
exit 1
fi
if [[ -z "${JSON}" ]]; then
echo "Script requires JSON env to be set to path of JSON binary"
exit 1
fi
readonly CURL="curl --retry 5 -s -u ${DIGITAL_OCEAN_TOKEN}:"
function debug() {
echo "$@" >&2
}
function get_ssh_key_id() {
id=$($CURL "https://api.digitalocean.com/v2/account/keys" \
| $JSON ssh_keys \
| $JSON -c "this.name === \"$1\"" \
| $JSON 0.id)
[[ -z "$id" ]] && exit 1
echo "$id"
}
function create_droplet() {
local ssh_key_id="$1"
local box_name="$2"
local image_region="sfo1"
local ubuntu_image_slug="ubuntu-16-04-x64"
local box_size="1gb"
local data="{\"name\":\"${box_name}\",\"size\":\"${box_size}\",\"region\":\"${image_region}\",\"image\":\"${ubuntu_image_slug}\",\"ssh_keys\":[ \"${ssh_key_id}\" ],\"backups\":false}"
id=$($CURL -X POST -H 'Content-Type: application/json' -d "${data}" "https://api.digitalocean.com/v2/droplets" | $JSON droplet.id)
[[ -z "$id" ]] && exit 1
echo "$id"
}
function get_droplet_ip() {
local droplet_id="$1"
ip=$($CURL "https://api.digitalocean.com/v2/droplets/${droplet_id}" | $JSON "droplet.networks.v4[0].ip_address")
[[ -z "$ip" ]] && exit 1
echo "$ip"
}
function get_droplet_id() {
local droplet_name="$1"
id=$($CURL "https://api.digitalocean.com/v2/droplets?per_page=200" | $JSON "droplets" | $JSON -c "this.name === '${droplet_name}'" | $JSON "[0].id")
[[ -z "$id" ]] && exit 1
echo "$id"
}
function power_off_droplet() {
local droplet_id="$1"
local data='{"type":"power_off"}'
local response=$($CURL -X POST -H 'Content-Type: application/json' -d "${data}" "https://api.digitalocean.com/v2/droplets/${droplet_id}/actions")
local event_id=`echo "${response}" | $JSON action.id`
if [[ -z "${event_id}" ]]; then
debug "Got no event id, assuming already powered off."
debug "Response: ${response}"
return
fi
debug "Powered off droplet. Event id: ${event_id}"
debug -n "Waiting for droplet to power off"
while true; do
local event_status=`$CURL "https://api.digitalocean.com/v2/droplets/${droplet_id}/actions/${event_id}" | $JSON action.status`
if [[ "${event_status}" == "completed" ]]; then
break
fi
debug -n "."
sleep 10
done
debug ""
}
function power_on_droplet() {
local droplet_id="$1"
local data='{"type":"power_on"}'
local event_id=`$CURL -X POST -H 'Content-Type: application/json' -d "${data}" "https://api.digitalocean.com/v2/droplets/${droplet_id}/actions" | $JSON action.id`
debug "Powered on droplet. Event id: ${event_id}"
if [[ -z "${event_id}" ]]; then
debug "Got no event id, assuming already powered on"
return
fi
debug -n "Waiting for droplet to power on"
while true; do
local event_status=`$CURL "https://api.digitalocean.com/v2/droplets/${droplet_id}/actions/${event_id}" | $JSON action.status`
if [[ "${event_status}" == "completed" ]]; then
break
fi
debug -n "."
sleep 10
done
debug ""
}
function get_image_id() {
local snapshot_name="$1"
local image_id=""
if ! response=$($CURL "https://api.digitalocean.com/v2/images?per_page=200"); then
echo "Failed to get image listing. ${response}"
return 1
fi
if ! image_id=$(echo "$response" \
| $JSON images \
| $JSON -c "this.name === \"${snapshot_name}\"" 0.id); then
echo "Failed to parse curl response: ${response}"
return 1
fi
if [[ -z "${image_id}" ]]; then
echo "Failed to get image id of ${snapshot_name}. reponse: ${response}"
return 1
fi
echo "${image_id}"
}
function snapshot_droplet() {
local droplet_id="$1"
local snapshot_name="$2"
local data="{\"type\":\"snapshot\",\"name\":\"${snapshot_name}\"}"
local event_id=`$CURL -X POST -H 'Content-Type: application/json' -d "${data}" "https://api.digitalocean.com/v2/droplets/${droplet_id}/actions" | $JSON action.id`
debug "Droplet snapshotted as ${snapshot_name}. Event id: ${event_id}"
debug -n "Waiting for snapshot to complete"
while true; do
if ! response=$($CURL "https://api.digitalocean.com/v2/droplets/${droplet_id}/actions/${event_id}"); then
echo "Could not get action status. ${response}"
continue
fi
if ! event_status=$(echo "${response}" | $JSON action.status); then
echo "Could not parse action.status from response. ${response}"
continue
fi
if [[ "${event_status}" == "completed" ]]; then
break
fi
debug -n "."
sleep 10
done
debug "! done"
if ! image_id=$(get_image_id "${snapshot_name}"); then
return 1
fi
echo "${image_id}"
}
function destroy_droplet() {
local droplet_id="$1"
# TODO: check for 204 status
$CURL -X DELETE "https://api.digitalocean.com/v2/droplets/${droplet_id}"
debug "Droplet destroyed"
debug ""
}
function transfer_image() {
local image_id="$1"
local region_slug="$2"
local data="{\"type\":\"transfer\",\"region\":\"${region_slug}\"}"
local event_id=`$CURL -X POST -H 'Content-Type: application/json' -d "${data}" "https://api.digitalocean.com/v2/images/${image_id}/actions" | $JSON action.id`
echo "${event_id}"
}
function wait_for_image_event() {
local image_id="$1"
local event_id="$2"
debug -n "Waiting for ${event_id}"
while true; do
local event_status=`$CURL "https://api.digitalocean.com/v2/images/${image_id}/actions/${event_id}" | $JSON action.status`
if [[ "${event_status}" == "completed" ]]; then
break
fi
debug -n "."
sleep 10
done
debug ""
}
function transfer_image_to_all_regions() {
local image_id="$1"
xfer_events=()
image_regions=(ams2) ## sfo1 is where the image is created
for image_region in ${image_regions[@]}; do
xfer_event=$(transfer_image ${image_id} ${image_region})
echo "Image transfer to ${image_region} initiated. Event id: ${xfer_event}"
xfer_events+=("${xfer_event}")
sleep 1
done
echo "Image transfer initiated, but they will take some time to get transferred."
for xfer_event in ${xfer_events[@]}; do
$vps wait_for_image_event "${image_id}" "${xfer_event}"
done
}
if [[ $# -lt 1 ]]; then
debug "<command> <params...>"
exit 1
fi
case $1 in
get_ssh_key_id)
get_ssh_key_id "${@:2}"
;;
create)
create_droplet "${@:2}"
;;
get_id)
get_droplet_id "${@:2}"
;;
get_ip)
get_droplet_ip "${@:2}"
;;
power_on)
power_on_droplet "${@:2}"
;;
power_off)
power_off_droplet "${@:2}"
;;
snapshot)
snapshot_droplet "${@:2}"
;;
destroy)
destroy_droplet "${@:2}"
;;
transfer_image_to_all_regions)
transfer_image_to_all_regions "${@:2}"
;;
*)
echo "Unknown command $1"
exit 1
esac
-102
View File
@@ -1,102 +0,0 @@
#!/bin/bash
set -euv -o pipefail
readonly SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly arg_provider="${1:-generic}"
readonly arg_infraversionpath="${SOURCE_DIR}/${2:-}"
function die {
echo $1
exit 1
}
export DEBIAN_FRONTEND=noninteractive
apt-get -o Dpkg::Options::="--force-confdef" update -y
apt-get -o Dpkg::Options::="--force-confdef" dist-upgrade -y
echo "==> Installing required packages"
debconf-set-selections <<< 'mysql-server mysql-server/root_password password password'
debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password password'
# this enables automatic security upgrades (https://help.ubuntu.com/community/AutomaticSecurityUpdates)
apt-get -y install \
acl \
awscli \
build-essential \
cron \
curl \
dmsetup \
iptables \
logrotate \
mysql-server-5.7 \
nginx-full \
openssh-server \
pwgen \
rcconf \
swaks \
unattended-upgrades \
unbound
echo "==> Installing node.js"
mkdir -p /usr/local/node-6.9.2
curl -sL https://nodejs.org/dist/v6.9.2/node-v6.9.2-linux-x64.tar.gz | tar zxvf - --strip-components=1 -C /usr/local/node-6.9.2
ln -sf /usr/local/node-6.9.2/bin/node /usr/bin/node
ln -sf /usr/local/node-6.9.2/bin/npm /usr/bin/npm
apt-get install -y python # Install python which is required for npm rebuild
[[ "$(python --version 2>&1)" == "Python 2.7."* ]] || die "Expecting python version to be 2.7.x"
# https://docs.docker.com/engine/installation/linux/ubuntulinux/
echo "==> Installing Docker"
# create systemd drop-in file
mkdir -p /etc/systemd/system/docker.service.d
echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=devicemapper" > /etc/systemd/system/docker.service.d/cloudron.conf
curl -sL https://download.docker.com/linux/ubuntu/dists/xenial/pool/stable/amd64/docker-ce_17.03.1~ce-0~ubuntu-xenial_amd64.deb -o /tmp/docker.deb
# apt install with install deps (as opposed to dpkg -i)
apt install -y /tmp/docker.deb
rm /tmp/docker.deb
storage_driver=$(docker info | grep "Storage Driver" | sed 's/.*: //')
if [[ "${storage_driver}" != "devicemapper" ]]; then
echo "Docker is using "${storage_driver}" instead of devicemapper"
exit 1
fi
echo "==> Enable memory accounting"
apt-get -y install grub2
sed -e 's/^GRUB_CMDLINE_LINUX="\(.*\)"$/GRUB_CMDLINE_LINUX="\1 cgroup_enable=memory swapaccount=1 panic_on_oops=1 panic=5"/' -i /etc/default/grub
update-grub
echo "==> Downloading docker images"
if [ ! -f "${arg_infraversionpath}/infra_version.js" ]; then
echo "No infra_versions.js found"
exit 1
fi
images=$(node -e "var i = require('${arg_infraversionpath}/infra_version.js'); console.log(i.baseImages.join(' '), Object.keys(i.images).map(function (x) { return i.images[x].tag; }).join(' '));")
echo -e "\tPulling docker images: ${images}"
for image in ${images}; do
docker pull "${image}"
done
echo "==> Install collectd"
if ! apt-get install -y collectd collectd-utils; then
# FQDNLookup is true in default debian config. The box code has a custom collectd.conf that fixes this
echo "Failed to install collectd. Presumably because of http://mailman.verplant.org/pipermail/collectd/2015-March/006491.html"
sed -e 's/^FQDNLookup true/FQDNLookup false/' -i /etc/collectd/collectd.conf
fi
# Disable bind for good measure (on online.net, kimsufi servers these are pre-installed and conflicts with unbound)
systemctl stop bind9 || true
systemctl disable bind9 || true
# on ovh images dnsmasq seems to run by default
systemctl stop dnsmasq || true
systemctl disable dnsmasq || true
-56
View File
@@ -1,56 +0,0 @@
#!/usr/bin/env node
'use strict';
require('supererror')({ splatchError: true });
// remove timestamp from debug() based output
require('debug').formatArgs = function formatArgs(args) {
args[0] = this.namespace + ' ' + args[0];
};
var appHealthMonitor = require('./src/apphealthmonitor.js'),
async = require('async'),
config = require('./src/config.js'),
ldap = require('./src/ldap.js'),
server = require('./src/server.js');
console.log();
console.log('==========================================');
console.log(' Cloudron will use the following settings ');
console.log('==========================================');
console.log();
console.log(' Environment: ', config.CLOUDRON ? 'CLOUDRON' : 'TEST');
console.log(' Version: ', config.version());
console.log(' Admin Origin: ', config.adminOrigin());
console.log(' Appstore API server origin: ', config.apiServerOrigin());
console.log(' Appstore Web server origin: ', config.webServerOrigin());
console.log();
console.log('==========================================');
console.log();
async.series([
server.start,
ldap.start,
appHealthMonitor.start,
], function (error) {
if (error) {
console.error('Error starting server', error);
process.exit(1);
}
console.log('Cloudron is up and running');
});
var NOOP_CALLBACK = function () { };
process.on('SIGINT', function () {
server.stop(NOOP_CALLBACK);
ldap.stop(NOOP_CALLBACK);
setTimeout(process.exit.bind(process), 3000);
});
process.on('SIGTERM', function () {
server.stop(NOOP_CALLBACK);
ldap.stop(NOOP_CALLBACK);
setTimeout(process.exit.bind(process), 3000);
});
-16
View File
@@ -1,16 +0,0 @@
#!/usr/bin/env node
'use strict';
var sendFailureLogs = require('./src/logcollector').sendFailureLogs;
function main() {
if (process.argv.length !== 3) return console.error('Usage: crashnotifier.js <processName>');
var processName = process.argv[2];
console.log('Started crash notifier for', processName);
sendFailureLogs(processName, { unit: processName });
}
main();
Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

-319
View File
@@ -1,319 +0,0 @@
# Overview
Addons are services like database, authentication, email, caching that are part of the
Cloudron runtime. Setup, provisioning, scaling and maintanence of addons is taken care of
by the runtime.
The fundamental idea behind addons is to allow sharing of Cloudron resources across applications.
For example, a single MySQL server instance can be used across multiple apps. The Cloudron
runtime sets up addons in such a way that apps are isolated from each other.
# Using Addons
Addons are opt-in and must be specified in the [Cloudron Manifest](/references/manifest.html).
When the app runs, environment variables contain the necessary information to access the addon.
For example, the mysql addon sets the `MYSQL_URL` environment variable which is the
connection string that can be used to connect to the database.
When working with addons, developers need to remember the following:
* Environment variables are subject to change every time the app restarts. This can happen if the
Cloudron is rebooted or restored or the app crashes or an addon is re-provisioned. For this reason,
applications must never cache the value of environment variables across restarts.
* Addons must be setup or updated on each application start up. Most applications use DB migration frameworks
for this purpose to setup and update the DB schema.
* Addons are configured in the [addons section](/references/manifest.html#addons) of the manifest as below:
```
{
...
"addons": {
"oauth": { },
"redis" : { }
}
}
```
# All addons
## email
This addon allows an app to send and recieve emails on behalf of the user. The intended use case is webmail applications.
If an app wants to send mail (e.g notifications), it must use the [sendmail](/references/addons#sendmail)
addon. If the app wants to receive email (e.g user replying to notification), it must use the
[recvmail](/references/addons#recvmail) addon instead.
Apps using the IMAP and ManageSieve services below must be prepared to accept self-signed certificates (this is not a problem
because these are addresses internal to the Cloudron).
Exported environment variables:
```
MAIL_SMTP_SERVER= # SMTP server IP or hostname. Supports STARTTLS (TLS upgrade is enforced).
MAIL_SMTP_PORT= # SMTP server port
MAIL_IMAP_SERVER= # IMAP server IP or hostname. TLS required.
MAIL_IMAP_PORT= # IMAP server port
MAIL_SIEVE_SERVER= # ManageSieve server IP or hostname. TLS required.
MAIL_SIEVE_PORT= # ManageSieve server port
MAIL_DOMAIN= # Domain of the mail server
```
## ldap
This addon provides LDAP based authentication via LDAP version 3.
Exported environment variables:
```
LDAP_SERVER= # ldap server IP
LDAP_PORT= # ldap server port
LDAP_URL= # ldap url of the form ldap://ip:port
LDAP_USERS_BASE_DN= # ldap users base dn of the form ou=users,dc=cloudron
LDAP_GROUPS_BASE_DN= # ldap groups base dn of the form ou=groups,dc=cloudron
LDAP_BIND_DN= # DN to perform LDAP requests
LDAP_BIND_PASSWORD= # Password to perform LDAP requests
```
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `ldapsearch` client within the context of the app:
```
cloudron exec
# list users
> ldapsearch -x -h "${LDAP_SERVER}" -p "${LDAP_PORT}" -b "${LDAP_USERS_BASE_DN}"
# list users with authentication (Substitute username and password below)
> ldapsearch -x -D cn=<username>,${LDAP_USERS_BASE_DN} -w <password> -h "${LDAP_SERVER}" -p "${LDAP_PORT}" -b "${LDAP_USERS_BASE_DN}"
# list admins
> ldapsearch -x -h "${LDAP_SERVER}" -p "${LDAP_PORT}" -b "${LDAP_USERS_BASE_DN}" "memberof=cn=admins,${LDAP_GROUPS_BASE_DN}"
# list groups
> ldapsearch -x -h "${LDAP_SERVER}" -p "${LDAP_PORT}" -b "${LDAP_GROUPS_BASE_DN}"
```
## localstorage
Since all Cloudron apps run within a read-only filesystem, this addon provides a writeable folder under `/app/data/`.
All contents in that folder are included in the backup. On first run, this folder will be empty. File added in this path
as part of the app's image (Dockerfile) won't be present. A common pattern is to create the directory structure required
the app as part of the app's startup script.
The permissions and ownership of data within that directory are not guranteed to be preserved. For this reason, each app
has to restore permissions as required by the app as part of the app's startup script.
If the app is running under the recommeneded `cloudron` user, this can be achieved with:
```
chown -R cloudron:cloudron /app/data
```
## mongodb
By default, this addon provide mongodb 2.6.3.
Exported environment variables:
```
MONGODB_URL= # mongodb url
MONGODB_USERNAME= # username
MONGODB_PASSWORD= # password
MONGODB_HOST= # server IP/hostname
MONGODB_PORT= # server port
MONGODB_DATABASE= # database name
```
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `mongo` shell within the context of the app:
```
cloudron exec
# mongo -u "${MONGODB_USERNAME}" -p "${MONGODB_PASSWORD}" ${MONGODB_HOST}:${MONGODB_PORT}/${MONGODB_DATABASE}
```
## mysql
By default, this addon provides a single database on MySQL 5.6.19. The database is already created and the application
only needs to create the tables.
Exported environment variables:
```
MYSQL_URL= # the mysql url (only set when using a single database, see below)
MYSQL_USERNAME= # username
MYSQL_PASSWORD= # password
MYSQL_HOST= # server IP/hostname
MYSQL_PORT= # server port
MYSQL_DATABASE= # database name (only set when using a single database, see below)
```
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `mysql` client within the context of the app:
```
cloudron exec
> mysql --user=${MYSQL_USERNAME} --password=${MYSQL_PASSWORD} --host=${MYSQL_HOST} ${MYSQL_DATABASE}
```
The `multipleDatabases` option can be set to `true` if the app requires more than one database. When enabled,
the following environment variables are injected:
```
MYSQL_DATABASE_PREFIX= # prefix to use to create databases
```
## oauth
The Cloudron OAuth 2.0 provider can be used in an app to implement Single Sign-On.
Exported environment variables:
```
OAUTH_CLIENT_ID= # client id
OAUTH_CLIENT_SECRET= # client secret
```
The callback url required for the OAuth transaction can be contructed from the environment variables below:
```
APP_DOMAIN= # hostname of the app
APP_ORIGIN= # origin of the app of the form https://domain
API_ORIGIN= # origin of the OAuth provider of the form https://my-cloudrondomain
```
OAuth2 URLs can be constructed as follows:
```
AuthorizationURL = ${API_ORIGIN}/api/v1/oauth/dialog/authorize # see above for API_ORIGIN
TokenURL = ${API_ORIGIN}/api/v1/oauth/token
```
The token obtained via OAuth has a restricted scope wherein they can only access the [profile API](/references/api.html#profile). This restriction
is so that apps cannot make undesired changes to the user's Cloudron.
We currently provide OAuth2 integration for Ruby [omniauth](https://git.cloudron.io/cloudron/omniauth-cloudron) and Node.js [passport](https://git.cloudron.io/cloudron/passport-cloudron).
## postgresql
By default, this addon provides PostgreSQL 9.4.4.
Exported environment variables:
```
POSTGRESQL_URL= # the postgresql url
POSTGRESQL_USERNAME= # username
POSTGRESQL_PASSWORD= # password
POSTGRESQL_HOST= # server name
POSTGRESQL_PORT= # server port
POSTGRESQL_DATABASE= # database name
```
The postgresql addon whitelists the hstore and pg_trgm extensions to be installable by the database owner.
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `psql` client within the context of the app:
```
cloudron exec
> PGPASSWORD=${POSTGRESQL_PASSWORD} psql -h ${POSTGRESQL_HOST} -p ${POSTGRESQL_PORT} -U ${POSTGRESQL_USERNAME} -d ${POSTGRESQL_DATABASE}
```
## recvmail
The recvmail addon can be used to receive email for the application.
Exported environment variables:
```
MAIL_IMAP_SERVER= # the IMAP server. this can be an IP or DNS name
MAIL_IMAP_PORT= # the IMAP server port
MAIL_IMAP_USERNAME= # the username to use for authentication
MAIL_IMAP_PASSWORD= # the password to use for authentication
MAIL_TO= # the "To" address to use
MAIL_DOMAIN= # the mail for which email will be received
```
The IMAP server only accepts TLS connections. The app must be prepared to accept self-signed certs (this is not a problem because the
imap address is internal to the Cloudron).
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `openssl` tool within the context of the app:
```
cloudron exec
> openssl s_client -connect "${MAIL_IMAP_SERVER}:${MAIL_IMAP_PORT}" -crlf
```
The IMAP command `? LOGIN username password` can then be used to test the authentication.
## redis
By default, this addon provides redis 2.8.13. The redis is configured to be persistent and data is preserved across updates
and restarts.
Exported environment variables:
```
REDIS_URL= # the redis url
REDIS_HOST= # server name
REDIS_PORT= # server port
REDIS_PASSWORD= # password
```
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `redis-cli` client within the context of the app:
```
cloudron exec
> redis-cli -h "${REDIS_HOST}" -p "${REDIS_PORT}" -a "${REDIS_PASSWORD}"
```
## scheduler
The scheduler addon can be used to run tasks at periodic intervals (cron).
Scheduler can be configured as below:
```
"scheduler": {
"update_feeds": {
"schedule": "*/5 * * * *",
"command": "/app/code/update_feed.sh"
}
}
```
In the above example, `update_feeds` is the name of the task and is an arbitrary string.
`schedule` values must fall within the following ranges:
* Minutes: 0-59
* Hours: 0-23
* Day of Month: 1-31
* Months: 0-11
* Day of Week: 0-6
_NOTE_: scheduler does not support seconds
`schedule` supports ranges (like standard cron):
* Asterisk. E.g. *
* Ranges. E.g. 1-3,5
* Steps. E.g. */2
`command` is executed through a shell (sh -c). The command runs in the same launch environment
as the application. Environment variables, volumes (`/tmp` and `/run`) are all
shared with the main application.
If a task is still running when a new instance of the task is scheduled to be started, the previous
task instance is killed.
## sendmail
The sendmail addon can be used to send email from the application.
Exported environment variables:
```
MAIL_SMTP_SERVER= # the mail server (relay) that apps can use. this can be an IP or DNS name
MAIL_SMTP_PORT= # the mail server port
MAIL_SMTP_USERNAME= # the username to use for authentication as well as the `from` username when sending emails
MAIL_SMTP_PASSWORD= # the password to use for authentication
MAIL_FROM= # the "From" address to use
MAIL_DOMAIN= # the domain name to use for email sending (i.e username@domain)
```
The SMTP server does not require STARTTLS. If STARTTLS is used, the app must be prepared to accept self-signed certs.
For debugging, [cloudron exec](https://www.npmjs.com/package/cloudron) can be used to run the `swaks` tool within the context of the app:
```
cloudron exec
> swaks --server "${MAIL_SMTP_SERVER}" -p "${MAIL_SMTP_PORT}" --from "${MAIL_SMTP_USERNAME}@${MAIL_DOMAIN}" --body "Test mail from cloudron app at $(hostname -f)" --auth-user "${MAIL_SMTP_USERNAME}" --auth-password "${MAIL_SMTP_PASSWORD}"
```
File diff suppressed because it is too large Load Diff
-87
View File
@@ -1,87 +0,0 @@
# Introduction
The Cloudron platform is designed to easily install and run web applications.
The application architecture is designed to let the Cloudron take care of system
operations like updates, backups, firewalls, domain management, certificate management
etc. This allows app developers to focus on their application logic instead of deployment.
At a high level, an application provides an `image` and a `manifest`. The image is simply
a docker image that is a bundle of the application code and it's dependencies. The manifest
file specifies application runtime requirements like database type and authentication scheme.
It also provides meta information for display purposes in the [Cloudron Store](/appstore.html)
like the title, icon and pricing.
Web applications like blogs, wikis, password managers, code hosting, document editing,
file syncers, notes, email, forums are a natural fit for the Cloudron. Decentralized "social"
networks are also good app candidates for the Cloudron.
# Image
Application images are created using [Docker](https://www.docker.io). Docker provides a way
to package (and containerize) the application as a filesystem which contains it's code, system libraries
and just about anything the app requires. This flexible approach allows the application to use just
about any language or framework.
Application images are instantiated as `containers`. Cloudron can run one or more isolated instances
of the same application as one or more containers.
Containerizing your application provides the following benefits:
* Apps run in the familiar environment that they were packaged for and can have libraries
and packages that are independent of the host OS.
* Containers isolate applications from one another.
The [base image](/references/baseimage.html) is the parent of all app images.
# Cloudron Manifest
Each app provides a `CloudronManifest.json` that specifies information required for the
`Cloudron Store` and for the installation of the image in the Cloudron.
Information required for container installation includes:
* List of `addons` like databases, caches, authentication mechanisms and file systems
* The http port on which the container is listening for incoming requests
* Additional TCP ports on which the application is listening to (for e.g., git, ssh,
irc protocols)
Information required for the Cloudron Store includes:
* Unique App Id
* Title
* Version
* Logo
See the [manifest reference](/references/manifest.html) for more information.
# Addons
Addons are services like database, authentication, email, caching that are part of the
Cloudron. Setup, provisioning, scaling and maintenance of addons is taken care of by the
Cloudron.
The fundamental idea behind addons is to allow resource sharing across applications.
For example, a single MySQL server instance can be used across multiple apps. The Cloudron
sets up addons in such a way that apps are isolated from each other.
Addons are opt-in and must be specified in the Cloudron Manifest. When the app runs, environment
variables contain the necessary information to access the addon. See the
[addon reference](/references/addons.html) for more information.
# Authentication
The Cloudron provides a centralized dashboard to manage users, roles and permissions. Applications
do not create or manage user credentials on their own and instead use one of the various
authentication strategies provided by the Cloudron.
Authentication strategies include OAuth 2.0, LDAP or Simple Auth. See the
[Authentication Reference](/references/authentication.html) for more information.
Authorizing users is application specific and it is only authentication that is delegated to the
Cloudron.
# Cloudron App Library
Cloudron App Library provides a market place to publish your app.
Submitting to the app library enables any Cloudron user to discover and install your application with a few clicks.
# What next?
* [Package an existing app for the Cloudron](/tutorials/packaging.html)
-105
View File
@@ -1,105 +0,0 @@
# Overview
Cloudron provides a centralized dashboard to manage users, roles and permissions. Applications
do not create or manage user credentials on their own and instead use one of the various
authentication strategies provided by the Cloudron.
Note that authentication only identifies a user and does not indicate if the user is authorized
to perform an action in the application. Authorizing users is application specific and must be
implemented by the application.
# Users & Admins
Cloudron user management is intentionally very simple. The owner (first user) of the
Cloudron is `admin` by default. The `admin` role allows one to install, uninstall and reconfigure
applications on the Cloudron.
A Cloudron `admin` can create one or more users. Cloudron users can login and use any of the installed
apps in the Cloudron. In general, adding a cloudron user is akin to adding a person from one's family
or organization or team because such users gain access to all apps in the Cloudron. Removing a user
immediately revokes access from all apps.
A Cloudron `admin` can give admin privileges to one or more Cloudron users.
Each Cloudron user has an unique `username` and an `email`.
# Strategies
Cloudron provides multiple authentication strategies.
* OAuth 2.0 provided by the [OAuth addon](/references/addons.html#oauth)
* LDAP provided by the [LDAP addon](/references/addons.html#ldap)
# Choosing a strategy
Applications can be broadly categorized based on their user management as follows:
* Multi-user aware
* Such apps have a full fledged user system and support multiple users and groups.
* These apps should use OAuth or LDAP.
* LDAP and OAuth APIs allow apps to detect if the user is a cloudron `admin`. Apps should use this flag
to show the application's admin panel for such users.
* No user
* Such apps have no concept of logged-in user.
* Single user
* Such apps only have a single user who is usually also the `admin`.
* These apps can use Simple Auth or LDAP since they can authenticate users with a simple HTTP or LDAP request.
* Such apps _must_ set the `singleUser` property in the manifest which will restrict login to a single user
(configurable through the Cloudron's admin panel).
# Public and Private apps
`Private` apps display content only when they have a signed-in user. These apps can choose one of the
authentication strategies listed above.
`Public` apps display content to any visiting user (e.g a blog). These apps have a `login` url to allow
the editors & admins to login. This path can be optionally set as the `configurePath` in the manifest for
discoverability (for example, some blogs hide the login link).
Some apps allow the user to choose `private` or `public` mode or some other combination. Such configuration
is done at app install time and cannot be changed using a settings interface. It is tempting to show the user
a configuration dialog on first installation to switch the modes. This, however, leads the user to believe that
this configuration can be changed at any time later. In the case where this setting can be changed dynamically
from a settings ui in the app, it's better to simply put some sensible defaults and let the user discover
the settings. In the case where such settings cannot be changed dynamically, it is best to simply publish two
separate apps in the Cloudron store each with a different configuration.
# External User Registration
Some apps allow external users to register and create accounts. For example, a public company chat that
can invite anyone to join or a blog allowing registered commenters.
Such applications must track Cloudron users and external registered users independently (for example, using a flag).
As a thumb rule, apps must provide separate login buttons for each of the possible user sources. Such a design prevents
external users from (inadvertently) spoofing Cloudron users.
Naively handling user registration enables attacks of the following kind:
* An external user named `foo` registers in the app.
* A LDAP user named `foo` is later created on the Cloudron.
* When a user named `foo` logs in, the app cannot determine the correct `foo` anymore. Making separate login buttons for each
login source clears the confusion for both the user and the app.
# Userid
The preferred approach to track users in an application is a uuid or the Cloudron `username`.
The `username` in Cloudron is unique and cannot be changed.
Tracking users using `email` field is error prone since that may be changed by the user anytime.
# Single Sign-on
Single sign-on (SSO) is a property where a user logged in one application automatically logs into
another application without having to re-enter his credentials. When applications implement the
OAuth strategy, they automatically take part in Cloudron SSO. When a user signs in one application with
OAuth, they will automatically log into any other app implementing OAuth.
Conversely, signing off from one app, logs them off from all the apps.
# Security
The LDAP and Simple Auth strategies require the user to provide their plain text passwords to the
application. This might be a cause of concern and app developers are thus highly encouraged to integrate
with OAuth. OAuth also has the advantage of supporting Single Sign On.
-94
View File
@@ -1,94 +0,0 @@
# Overview
The application's Dockerfile must specify the FROM base image to be `cloudron/base:0.10.0`.
The base image already contains most popular software packages including node, nginx, apache,
ruby, PHP. Using the base image greatly reduces the size of app images.
The goal of the base image is simply to provide pre-downloaded software packages. The packages
are not configured in any way and it's up to the application to configure them as they choose.
For example, while `apache` is installed, there are no meaningful site configurations that the
application can use.
# Packages
The following packages are part of the base image. If you need another version, you will have to
install it yourself.
* Apache 2.4.18
* Composer 1.2.0
* Go 1.6.4, 1.7.5 (install under `/usr/local/go-<version>`)
* Gunicorn 19.4.5
* Java 1.8
* Maven 3.3.9
* Mongo 2.6.10
* MySQL Client 5.7.17
* nginx 1.10.0
* Node 0.10.48, 0.12.18, 4.7.3, 6.9.5 (installed under `/usr/local/node-<version>`) [more information](#node-js)
* Perl 5.22.1
* PHP 7.0.13
* Postgresql client 9.5.4
* Python 2.7.12
* Redis 3.0.6
* Ruby 2.3.1
* sqlite3 3.11.0
* Supervisor 3.2.0
* uwsgi 2.0.12
# Inspecting the base image
The base image can be inspected by installing [Docker](https://docs.docker.com/installation/).
Once installed, pull down the base image locally using the following command:
```
docker pull cloudron/base:0.10.0
```
To inspect the base image:
```
docker run -ti cloudron/base:0.10.0 /bin/bash
```
*Note:* Please use `docker 1.9.0` or above to pull the base image. Doing otherwise results in a base
image with an incorrect image id. The image id of `cloudron/base:0.10.0` is `5ec8ca8525be`.
# The `cloudron` user
The base image contains a user named `cloudron` that apps can use to run their app.
It is good security practice to run apps as a non-previleged user.
# Env vars
The following environment variables are set as part of the application runtime.
## API_ORIGIN
API_ORIGIN is set to the HTTP(S) origin of this Cloudron's API. For example,
`https://my-girish.cloudron.us`.
## APP_DOMAIN
APP_DOMAIN is set to the domain name of the application. For example, `app-girish.cloudron.us`.
## APP_ORIGIN
APP_ORIGIN is set to the HTTP(S) origin on the application. This is origin which the
user can use to reach the application. For example, `https://app-girish.cloudron.us`.
## CLOUDRON
CLOUDRON is always set to '1'. This is useful to write Cloudron specific code.
## WEBADMIN_ORIGIN
WEBADMIN_ORIGIN is set to the HTTP(S) origin of the Cloudron's web admin. For example,
`https://my-girish.cloudron.us`.
# Node.js
The base image comes pre-installed with various node.js versions.
They can be used by adding `ENV PATH /usr/local/node-<version>/bin:$PATH`.
See [Packages](/references/baseimage.html#packages) for available versions.
-47
View File
@@ -1,47 +0,0 @@
# Cloudron Button
The `Cloudron Button` allows anyone to install an application with
the click of a button on their Cloudron.
The button can be added to just about any website including the application's website
and README.md files in GitHub repositories.
## Prerequisites
The `Cloudron Button` is intended to work only for applications that have been
published on the Cloudron Store. The [basic tutorial](/tutorials/basic.html#publishing)
gives an overview of how to package and publish your application for the
Cloudron Store.
## HTML Snippet
```
<img src="https://cloudron.io/img/button32.png" href="https://cloudron.io/button.html?app=<appid>">
```
_Note_: Replace `<appid>` with your application's id.
## Markdown Snippet
```
[![Install](https://cloudron.io/img/button32.png)](https://cloudron.io/button.html?app=<appid>)
```
_Note_: Replace `<appid>` with your application's id.
## Button Height
The button may be used in different heights - 32, 48 and 64 pixels.
[![Install](/img/button32.png)](https://cloudron.io/button.html?app=io.gogs.cloudronapp)
[![Install](/img/button48.png)](https://cloudron.io/button.html?app=io.gogs.cloudronapp)
[![Install](/img/button64.png)](https://cloudron.io/button.html?app=io.gogs.cloudronapp)
or as SVG
[![Install](/img/button.svg)](https://cloudron.io/button.html?app=io.gogs.cloudronapp)
_Note_: Clicking the buttons above will install [Gogs](http://gogs.io/) on your Cloudron.
-441
View File
@@ -1,441 +0,0 @@
# Overview
Every Cloudron Application contains a `CloudronManifest.json`.
The manifest contains two categories of information:
* Information about displaying the app on the Cloudron Store. For example,
the title, author information, description etc
* Information for installing the app on the Cloudron. This includes fields
like httpPort, tcpPorts.
A CloudronManifest.json can **only** contain fields that are listed as part of this
specification. The Cloudron Store and the Cloudron *may* reject applications that have
extra fields.
Here is an example manifest:
```
{
"id": "com.example.test",
"title": "Example Application",
"author": "Girish Ramakrishnan <girish@cloudron.io>",
"description": "This is an example app",
"tagline": "A great beginning",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {}
},
"manifestVersion": 1,
"website": "https://www.example.com",
"contactEmail": "support@clourdon.io",
"icon": "file://icon.png",
"tags": [ "test", "collaboration" ],
"mediaLinks": [ "https://images.rapgenius.com/fd0175ef780e2feefb30055be9f2e022.520x343x1.jpg" ]
}
```
# Fields
## addons
Type: object
Required: no
Allowed keys
* [email](addons.html#email)
* [ldap](addons.html#ldap)
* [localstorage](addons.html#localstorage)
* [mongodb](addons.html#mongodb)
* [mysql](addons.html#mysql)
* [oauth](addons.html#oauth)
* [postgresql](addons.html#postgresql)
* [recvmail](addons.html#recvmail)
* [redis](addons.html#redis)
* [sendmail](addons.html#sendmail)
The `addons` object lists all the [addons](addons.html) and the addon configuration used by the application.
Example:
```
"addons": {
"localstorage": {},
"mongodb": {}
}
```
## author
Type: string
Required: yes
The `author` field contains the name and email of the app developer (or company).
Example:
```
"author": "Cloudron UG <girish@cloudron.io>"
```
## changelog
Type: markdown string
Required: no (required for submitting to the Cloudron Store)
The `changelog` field contains the changes in this version of the application. This string
can be a markdown style bulleted list.
Example:
```
"changelog": "* Add support for IE8 \n* New logo"
```
## contactEmail
Type: email
Required: yes
The `contactEmail` field contains the email address that Cloudron users can contact for any
bug reports and suggestions.
Example:
```
"contactEmail": "support@testapp.com"
```
## description
Type: markdown string
Required: yes
The `description` field contains a detailed description of the app. This information is shown
to the user when they install the app from the Cloudron Store.
Example:
```
"description": "This is a detailed description of this app."
```
A large `description` can be unweildy to manage and edit inside the CloudronManifest.json. For
this reason, the `description` can also contain a file reference. The Cloudron CLI tool fills up
the description from this file when publishing your application.
Example:
```
"description:": "file://DESCRIPTION.md"
```
## healthCheckPath
Type: url path
Required: yes
The `healthCheckPath` field is used by the Cloudron Runtime to determine if your app is running and
responsive. The app must return a 2xx HTTP status code as a response when this path is queried. In
most cases, the default "/" will suffice but there might be cases where periodically querying "/"
is an expensive operation. In addition, the app might want to use a specialized route should it
want to perform some specialized internal checks.
Example:
```
"healthCheckPath": "/"
```
## httpPort
Type: positive integer
Required: yes
The `httpPort` field contains the TCP port on which your app is listening for HTTP requests. This
is the HTTP port the Cloudron will use to access your app internally.
While not required, it is good practice to mark this port as `EXPOSE` in the Dockerfile.
Cloudron Apps are containerized and thus two applications can listen on the same port. In reality,
they are in different network namespaces and do not conflict with each other.
Note that this port has to be HTTP and not HTTPS or any other non-HTTP protocol. HTTPS proxying is
handled by the Cloudron platform (since it owns the certificates).
Example:
```
"httpPort": 8080
```
## icon
Type: local image filename
Required: no (required for submitting to the Cloudron Store)
The `icon` field is used to display the application icon/logo in the Cloudron Store. Icons are expected
to be square of size 256x256.
```
"icon": "file://icon.png"
```
## id
Type: reverse domain string
Required: yes
The `id` is a unique human friendly Cloudron Store id. This is similar to reverse domain string names used
as java package names. The convention is to base the `id` based on a domain that you own.
The Cloudron tooling allows you to build applications with any `id`. However, you will be unable to publish
the application if the id is already in use by another application.
```
"id": "io.cloudron.testapp"
```
## manifestVersion
Type: integer
Required: yes
`manifestVersion` specifies the version of the manifest and is always set to 1.
```
"manifestVersion": 1
```
## mediaLinks
Type: array of urls
Required: no (required for submitting to the Cloudron Store)
The `mediaLinks` field contains an array of links that the Cloudron Store uses to display a slide show of pictures of the application.
They have to be publicly reachable via `https` and should have an aspect ratio of 3 to 1.
For example `600px by 200px` (with/height).
```
"mediaLinks": [
"https://s3.amazonaws.com/cloudron-app-screenshots/org.owncloud.cloudronapp/556f6a1d82d5e27a7c4fca427ebe6386d373304f/2.jpg",
"https://images.rapgenius.com/fd0175ef780e2feefb30055be9f2e022.520x343x1.jpg"
]
```
## memoryLimit
Type: bytes (integer)
Required: no
The `memoryLimit` field is the maximum amount of memory (including swap) in bytes an app is allowed to consume before it
gets killed and restarted.
By default, all apps have a memoryLimit of 256MB. For example, to have a limit of 500MB,
```
"memoryLimit": 524288000
```
## maxBoxVersion
Type: semver string
Required: no
The `maxBoxVersion` field is the maximum box version that the app can possibly run on. Attempting to install the app on
a box greater than `maxBoxVersion` will fail.
This is useful when a new box release introduces features which are incompatible with the app. This situation is quite
unlikely and it is recommended to leave this unset.
## minBoxVersion
Type: semver string
Required: no
The `minBoxVersion` field is the minimum box version that the app can possibly run on. Attempting to install the app on
a box lesser than `minBoxVersion` will fail.
This is useful when the app relies on features that are only available from a certain version of the box. If unset, the
default value is `0.0.1`.
## postInstallMessage
Type: markdown string
Required: no
The `postInstallMessageField` is a message that is displayed to the user after an app is installed.
The intended use of this field is to display some post installation steps that the user has to carry out to
complete the installation. For example, displaying the default admin credentials and informing the user to
to change it.
The message can have the following special tags:
* `<sso> ... </sso>` - Content in `sso` blocks are shown if SSO enabled.
* `<nosso> ... </nosso>`- Content in `nosso` blocks are shows when SSO is disabled.
## optionalSso
Type: boolean
Required: no
The `optionalSso` field can be set to true for apps that can be installed optionally without using the Cloudron user management.
This only applies if any Cloudron auth related addons are used. When set, the Cloudron will not inject the auth related addon environment variables.
Any app startup scripts have to be able to deal with missing env variables in this case.
## tagline
Type: one-line string
Required: no (required for submitting to the Cloudron Store)
The `tagline` is used by the Cloudron Store to display a single line short description of the application.
```
"tagline": "The very best note keeper"
```
## tags
Type: Array of strings
Required: no (required for submitting to the Cloudron Store)
The `tags` are used by the Cloudron Store for filtering searches by keyword.
```
"tags": [ "git", "version control", "scm" ]
```
## targetBoxVersion
Type: semver string
Required: no
The `targetBoxVersion` field is the box version that the app was tested on. By definition, this version has to be greater
than the `minBoxVersion`.
The box uses this value to enable compatibility behavior of APIs. For example, an app sets the targetBoxVersion to 0.0.5
and is published on the store. Later, box version 0.0.10 introduces a new feature that conflicts with how apps used
to run in 0.0.5 (say SELinux was enabled for apps). When the box runs such an app, it ensures compatible behavior
and will disable the SELinux feature for the app.
If unspecified, this value defaults to `minBoxVersion`.
## tcpPorts
Type: object
Required: no
Syntax: Each key is the environment variable. Each value is an object containing `title`, `description` and `defaultValue`.
An optional `containerPort` may be specified.
The `tcpPorts` field provides information on the non-http TCP ports/services that your application is listening on. During
installation, the user can decide how these ports are exposed from their Cloudron.
For example, if the application runs an SSH server at port 29418, this information is listed here. At installation time,
the user can decide any of the following:
* Expose the port with the suggested `defaultValue` to the outside world. This will only work if no other app is being exposed at same port.
* Provide an alternate value on which the port is to be exposed to outside world.
* Disable the port/service.
To illustrate, the application lists the ports as below:
```
"tcpPorts": {
"SSH_PORT": {
"title": "SSH Port",
"description": "SSH Port over which repos can be pushed & pulled",
"defaultValue": 29418,
"containerPort": 22
}
},
```
In the above example:
* `SSH_PORT` is an app specific environment variable. Only strings, numbers and _ (underscore) are allowed. The author has to ensure that they don't clash with platform profided variable names.
* `title` is a short one line information about this port/service.
* `description` is a multi line description about this port/service.
* `defaultValue` is the recommended port value to be shown in the app installation UI.
* `containerPort` is the port that the app is listening on (recall that each app has it's own networking namespace).
In more detail:
* If the user decides to disable the SSH service, this environment variable `SSH_PORT` is absent. Applications _must_ detect this on
start up and disable these services.
* `SSH_PORT` is set to the value of the exposed port. Should the user choose to expose the SSH server on port 6000, then the
value of SSH_PORT is 6000.
* `defaultValue` is **only** used for display purposes in the app installation UI. This value is independent of the value
that the app is listening on. For example, the app can run an SSH server at port 22 but still recommend a value of 29418 to the user.
* `containerPort` is the port that the app is listening on. The Cloudron runtime will _bridge_ the user chosen external port
with the app specific `containerPort`. Cloudron Apps are containerized and each app has it's own networking namespace.
As a result, different apps can have the same `containerPort` value because these values are namespaced.
* The environment variable `SSH_PORT` may be used by the app to display external URLs. For example, the app might want to display
the SSH URL. In such a case, it would be incorrect to use the `containerPort` 22 or the `defaultValue` 29418 since this is not
the value chosen by the user.
* `containerPort` is optional and can be omitted, in which case the bridged port numbers are the same internally and externally.
Some apps use the same variable (in their code) for listen port and user visible display strings. When packaging these apps,
it might be simpler to listen on `SSH_PORT` internally. In such cases, the app can omit the `containerPort` value and should
instead reconfigure itself to listen internally on `SSH_PORT` on each start up.
## title
Type: string
Required: yes
The `title` is the primary application title displayed on the Cloudron Store.
Example:
```
"title": "Gitlab"
```
## version
Type: semver string
Required: yes
The `version` field specifies a [semver](http://semver.org/) string. The version is used by the Cloudron to compare versions and to
determine if an update is available.
Example:
```
"version": "1.1.0"
```
## website
Type: url
Required: yes
The `website` field is a URL where the user can read more about the application.
Example:
```
"website": "https://example.com/myapp"
```
-61
View File
@@ -1,61 +0,0 @@
# Configuration Recipes
## nginx
`nginx` is often used as a reverse proxy in front of the application, to dispatch to different backend programs based on the request route or other characteristics. In such a case it is recommended to run nginx and the application through a process manager like `supervisor`.
Example nginx supervisor configuration file:
```
[program:nginx]
directory=/tmp
command=/usr/sbin/nginx -g "daemon off;"
user=root
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
```
The nginx configuration, provided with the base image, can be used by adding an application specific config file under `/etc/nginx/sites-enabled/` when building the docker image.
```
ADD <app config file> /etc/nginx/sites-enabled/<app config file>
```
Since the base image nginx configuration is unpatched from the ubuntu package, the application configuration has to ensure nginx is using `/run/` instead of `/var/lib/nginx/` to support the read-only filesystem nature of a Cloudron application.
Example nginx app config file:
```
client_body_temp_path /run/client_body;
proxy_temp_path /run/proxy_temp;
fastcgi_temp_path /run/fastcgi_temp;
scgi_temp_path /run/scgi_temp;
uwsgi_temp_path /run/uwsgi_temp;
server {
listen 8000;
root /app/code/dist;
location /api/v1/ {
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}
```
## supervisor
Use this in the program's config:
```
[program:app]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
```
-510
View File
@@ -1,510 +0,0 @@
# Overview
The Cloudron platform can be installed on public cloud servers from EC2, Digital Ocean, Hetzner,
Linode, OVH, Scaleway, Vultr etc. Cloudron also runs well on a home server or company intranet.
If you run into any trouble following this guide, ask us at our [chat](https://chat.cloudron.io).
# Understand
Before installing the Cloudron, it is helpful to understand Cloudron's design. The Cloudron
intends to make self-hosting effortless. It takes care of updates, backups, firewall, dns setup,
certificate management etc. All app and user configuration is carried out using the web interface.
This approach to self-hosting means that the Cloudron takes complete ownership of the server and
only tracks changes that were made via the web interface. Any external changes made to the server
(i.e other than via the Cloudron web interface or API) may be lost across updates.
The Cloudron requires a domain name when it is installed. Apps are installed into subdomains.
The `my` subdomain is special and is the location of the Cloudron web interface. For this to
work, the Cloudron requires a way to programmatically configure the DNS entries of the domain.
Note that the Cloudron will never overwrite _existing_ DNS entries and refuse to install
apps on existing subdomains (so, it is safe to reuse an existing domain that runs other services).
# Cloud Server
DigitalOcean and EC2 (Amazon Web Services) are frequently tested by us.
Please use the below links to support us with referrals:
* [Amazon EC2](https://aws.amazon.com/ec2/)
* [DigitalOcean](https://m.do.co/c/933831d60a1e)
In addition to those, the Cloudron community has successfully installed the platform on those providers:
* [Amazon Lightsail](https://amazonlightsail.com/)
* [hosttech](https://www.hosttech.ch/?promocode=53619290)
* [Linode](https://www.linode.com/?r=f68d816692c49141e91dd4cef3305da457ac0f75)
* [OVH](https://www.ovh.com/)
* [Rosehosting](https://secure.rosehosting.com/clientarea/?affid=661)
* [Scaleway](https://www.scaleway.com/)
* [So you Start](https://www.soyoustart.com/)
* [Vultr](http://www.vultr.com/?ref=7110116-3B)
Please let us know if any of them requires tweaks or adjustments.
# Installing
## Create server
Create an `Ubuntu 16.04 (Xenial)` server with at-least `1gb` RAM and 20GB disk space.
Do not make any changes to vanilla ubuntu. Be sure to allocate a static IPv4 address
for your server.
Cloudron has a built-in firewall and ports are opened and closed dynamically, as and when
apps are installed, re-configured or removed. For this reason, be sure to open all TCP and
UDP traffic to the server and leave the traffic management to the Cloudron.
### Kimsufi
Be sure to check the "use the distribution kernel" checkbox in the personalized installation mode.
### Linode
Since Linode does not manage SSH keys, be sure to add the public key to
`/root/.ssh/authorized_keys`.
## Run setup
SSH into your server and run the following commands:
```
wget https://cloudron.io/cloudron-setup
chmod +x cloudron-setup
./cloudron-setup --provider <azure|digitalocean|ec2|lightsail|linode|ovh|rosehosting|scaleway|vultr|generic>
```
The setup will take around 10-15 minutes.
**cloudron-setup** takes the following arguments:
* `--provider` is the name of your VPS provider. If the name is not on the list, simply
choose `generic`. In most cases, the `generic` provider mostly will work fine.
If the Cloudron does not complete initialization, it may mean that
we have to add some vendor specific quirks. Please open a
[bug report](https://git.cloudron.io/cloudron/box/issues) in that case.
Optional arguments for installation:
* `--tls-provider` is the name of the SSL/TLS certificate backend. Defaults to Let's encrypt.
Specifying `fallback` will setup the Cloudron to use the fallback wildcard certificate.
Initially a self-signed one is provided, which can be overwritten later in the admin interface.
This may be useful for non-public installations.
* `--data-dir` is the path where Cloudron will store platform and application data. Note: data
directory must be an `ext4` filesystem.
Optional arguments used for update and restore:
* `--version` is the version of Cloudron to install. By default, the setup script installs
the latest version. You can set this to an older version when restoring a Cloudron from a backup.
* `--restore-url` is a backup URL to restore from.
## Domain setup
Once the setup script completes, the server will reboot, then visit your server by its
IP address (`https://ip`) to complete the installation.
The setup website will show a certificate warning. Accept the self-signed certificate
and proceed to the domain setup.
Currently, only subdomains of the [Public Suffix List](https://publicsuffix.org/) are supported.
For example, `example.com`, `example.co.uk` will work fine. Choosing other non-registrable
domain names like `cloudron.example.com` will not work.
### Route 53
Create root or IAM credentials and choose `Route 53` as the DNS provider.
* For root credentials:
* In AWS Console, under your name in the menu bar, click `Security Credentials`
* Click on `Access Keys` and create a key pair.
* For IAM credentials:
* You can use the following policy to create IAM credentials:
```
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:*",
"Resource": [
"arn:aws:route53:::hostedzone/<hosted zone id>"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:GetChange"
],
"Resource": [
"*"
]
}
]
}
```
### Digital Ocean
Create an API token with read+write access and choose `Digital Ocean` as the DNS provider.
### Other
If your domain *does not* use Route 53 or Digital Ocean, setup a wildcard (`*`) DNS `A` record that points to the
IP of the server created above. If your DNS provider has an API, please open an
[issue](https://git.cloudron.io/cloudron/box/issues) and we may be able to support it.
## Finish Setup
Once the domain setup is done, the Cloudron will configure the DNS and get a SSL certificate. It will automatically redirect to `https://my.<domain>`.
# Backups
The Cloudron creates encrypted backups once a day. Each app is backed up independently and these
backups have the prefix `app_`. The platform state is backed up independently with the
prefix `box_`.
By default, backups reside in `/var/backups`. Please note that having backups reside in the same
physical machine as the Cloudron server instance is dangerous and it must be changed to
an external storage location like `S3` as soon as possible.
## Amazon S3
Provide S3 backup credentials in the `Settings` page and leave the endpoint field empty.
Create a bucket in S3 (You have to have an account at [AWS](https://aws.amazon.com/)). The bucket can be setup to periodically delete old backups by
adding a lifecycle rule using the AWS console. S3 supports both permanent deletion
or moving objects to the cheaper Glacier storage class based on an age attribute.
With the current daily backup schedule a setting of two days should be sufficient
for most use-cases.
* For root credentials:
* In AWS Console, under your name in the menu bar, click `Security Credentials`
* Click on `Access Keys` and create a key pair.
* For IAM credentials:
* You can use the following policy to create IAM credentials:
```
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::<your bucket name>",
"arn:aws:s3:::<your bucket name>/*"
]
}
]
}
```
The `Encryption key` is an arbitrary passphrase used to encrypt the backups. Keep the passphrase safe; it is
required to decrypt the backups when restoring the Cloudron.
## Minio S3
[Minio](https://minio.io/) is a distributed object storage server, providing the same API as Amazon S3.
Since Cloudron supports S3, any API compatible solution should be supported as well, if this is not the case, let us know.
Minio can be setup, by following the [installation instructions](https://docs.minio.io/) on any server, which is reachable by the Cloudron.
Do not setup Minio on the same server as the Cloudron, this will inevitably result in data loss, if backups are stored on the same instance.
Once setup, minio will print the necessary information, like login credentials, region and endpoints in its logs.
```
$ ./minio server ./storage
Endpoint: http://192.168.10.113:9000 http://127.0.0.1:9000
AccessKey: GFAWYNJEY7PUSLTHYHT6
SecretKey: /fEWk66E7GsPnzE1gohqKDovaytLcxhr0tNWnv3U
Region: us-east-1
```
First create a new bucket for the backups, using the minio commandline tools or the webinterface. The bucket has to have **read and write** permissions.
The information to be copied to the Cloudron's backup settings form may look similar to:
<img src="/docs/img/minio_backup_config.png" class="shadow"><br/>
The `Encryption key` is an arbitrary passphrase used to encrypt the backups. Keep the passphrase safe; it is
required to decrypt the backups when restoring the Cloudron.
# Email
Cloudron has a built-in email server. By default, it only sends out email on behalf of apps
(for example, password reset or notification). You can enable the email server for sending
and receiving mail on the `settings` page. This feature is only available if you have setup
a DNS provider like Digital Ocean or Route53.
Your server's IP plays a big role in how emails from our Cloudron get handled. Spammers
frequently abuse public IP addresses and as a result your Cloudron might possibly start
out with a bad reputation. The good news is that most IP based blacklisting services cool
down over time. The Cloudron sets up DNS entries for SPF, DKIM, DMARC automatically and
reputation should be easy to get back.
## Checklist
* If you are unable to receive mail, first thing to check is if your VPS provider lets you
receive mail on port 25.
* Digital Ocean - New accounts frequently have port 25 blocked. Write to their support to
unblock your server.
* EC2, Lightsail & Scaleway - Edit your security group to allow email.
* Setup a Reverse DNS PTR record to be setup for the `my` subdomain.
**Note:** PTR records are a feature of your VPS provider and not your domain provider.
* You can verify the PTR record [https://mxtoolbox.com/ReverseLookup.aspx](here).
* AWS EC2 & Lightsail - Fill the [PTR request form](https://aws-portal.amazon.com/gp/aws/html-forms-controller/contactus/ec2-email-limit-rdns-request).
* Digital Ocean - Digital Ocean sets up a PTR record based on the droplet's name. So, simply rename
your droplet to `my.<domain>`. Note that some new Digital Ocean accounts have [port 25 blocked](https://www.digitalocean.com/community/questions/port-25-smtp-external-access).
* Linode - Follow this [guide](https://www.linode.com/docs/networking/dns/setting-reverse-dns).
* Scaleway - Edit your security group to allow email and [reboot the server](https://community.online.net/t/security-group-not-working/2096) for the change to take effect. You can also set a PTR record on the interface with your `my.<domain>`.
* Check if your IP is listed in any DNSBL list [here](http://multirbl.valli.org/) and [here](http://www.blk.mx).
In most cases, you can apply for removal of your IP by filling out a form at the DNSBL manager site.
* When using wildcard or manual DNS backends, you have to setup the DMARC, MX records manually.
* Finally, check your spam score at [mail-tester.com](https://www.mail-tester.com/). The Cloudron
should get 100%, if not please let us know.
# CLI Tool
The [Cloudron tool](https://git.cloudron.io/cloudron/cloudron-cli) is useful for managing
a Cloudron. <b class="text-danger">The Cloudron CLI tool has to be installed & run on a Laptop or PC</b>
Once installed, you can install, configure, list, backup and restore apps from the command line.
## Linux & OS X
Installing the CLI tool requires node.js and npm. The CLI tool can be installed using the following command:
```
npm install -g cloudron
```
Depending on your setup, you may need to run this as root.
On OS X, it is known to work with the `openssl` package from homebrew.
See [#14](https://git.cloudron.io/cloudron/cloudron-cli/issues/14) for more information.
## Windows
The CLI tool does not work on Windows. Please contact us on our [chat](https://chat.cloudron.io) if you want to help with Windows support.
# Updates
Apps installed from the Cloudron Store are automatically updated every night.
The Cloudron platform itself updates in two ways: update or upgrade.
### Update
An **update** is applied onto the running server instance. Such updates are performed
every night. You can also use the Cloudron UI to initiate an update immediately.
The Cloudron will always make a complete backup before attempting an update. In the unlikely
case an update fails, it can be [restored](/references/selfhosting.html#restore).
### Upgrade
An **upgrade** requires a new OS image. This process involves creating a new server from scratch
with the latest code and restoring it from the last backup.
To upgrade follow these steps closely:
* Create a new backup - `cloudron machine backup create`
* List the latest backup - `cloudron machine backup list`
* Make the backup available for the new cloudron instance:
* `S3` - When storing backup ins S3, make the latest box backup public - files starting with `box_` (from v0.94.0) or `backup_`. This can be done from the AWS S3 console as seen here:
<img src="/docs/img/aws_backup_public.png" class="shadow haze"><br/>
Copy the new public URL of the latest backup for use as the `--restore-url` below.
<img src="/docs/img/aws_backup_link.png" class="shadow haze"><br/>
* `File system` - When storing backups in `/var/backups`, you have to make the box and the app backups available to the new Cloudron instance's `/var/backups`. This can be achieved in a variety of ways depending on the situation: like scp'ing the backup files to the machine before installation, mounting the external backup hard drive into the new Cloudron's `/var/backup` OR downloading a copy of the backup using `cloudron machine backup download` and uploading them to the new machine. After doing so, pass `file:///var/backups/<path to box backup>` as the `--restore-url` below.
* Create a new Cloudron by following the [installing](/references/selfhosting.html#installing) section.
When running the setup script, pass in the `--encryption-key` and `--restore-url` flags.
The `--encryption-key` is the backup encryption key. It can be displayed with `cloudron machine info`
Similar to the initial installation, a Cloudron upgrade looks like:
```
$ ssh root@newserverip
> wget https://cloudron.io/cloudron-setup
> chmod +x cloudron-setup
> ./cloudron-setup --provider <digitalocean|ec2|generic|scaleway> --domain <example.com> --encryption-key <key> --restore-url <publicS3Url>
```
Note: When upgrading an old version of Cloudron (<= 0.94.0), pass the `--version 0.94.1` flag and then continue updating
from that.
* Finally, once you see the newest version being displayed in your Cloudron webinterface, you can safely delete the old server instance.
# Restore
To restore a Cloudron from a specific backup:
* Select the backup - `cloudron machine backup list`
* Make the backup public
* `S3` - Make the box backup publicly readable - files starting with `box_` (from v0.94.0) or `backup_`. This can be done from the AWS S3 console. Once the box has restored, you can make it private again.
* `File system` - When storing backups in `/var/backups`, you have to make the box and the app backups available to the new Cloudron instance's `/var/backups`. This can be achieved in a variety of ways depending on the situation: like scp'ing the backup files to the new machine before Cloudron installation OR mounting an external backup hard drive into the new Cloudron's `/var/backup` OR downloading a copy of the backup using `cloudron machine backup download` and uploading them to the new machine. After doing so, pass `file:///var/backups/<path to box backup>` as the `--restore-url` below.
* Create a new Cloudron by following the [installing](/references/selfhosting.html#installing) section.
When running the setup script, pass in the `version`, `encryption-key`, `domain` and `restore-url` flags.
The `version` field is the version of the Cloudron that the backup corresponds to (it is embedded
in the backup file name).
* Make the box backup private, once the upgrade is complete.
# Security
Security is a core feature of the Cloudron and we continue to push out updates to tighten the Cloudron's security policy. Our goal is that Cloudron users should be able to rely on Cloudron being secure out of the box without having to do manual configuration.
This section lists various security measures in place to protect the Cloudron.
## HTTP Security
* Cloudron admin has a CSP policy that prevents XSS attacks.
* Cloudron set various security related HTTP headers like `X-XSS-Protection`, `X-Download-Options`,
`X-Content-Type-Options`, `X-Permitted-Cross-Domain-Policies`, `X-Frame-Options` across all apps.
## SSL
* Cloudron enforces HTTPS across all apps. HTTP requests are automatically redirected to
HTTPS.
* The Cloudron automatically installs and renews certificates for your apps as needed. Should
installation of certificate fail for reasons beyond it's control, Cloudron admins will get a notification about it.
* Cloudron sets the `Strict-Transport-Security` header (HSTS) to protect apps against downgrade attacks
and cookie hijacking.
* Cloudron has A+ rating for SSL from [SSL Labs](https://cloudron.io/blog/2017-02-22-release-0.102.0.html).
## App isolation
* Apps are isolated completely from one another. One app cannot tamper with another apps' database or
local files. We achieve this using Linux Containers.
* Apps run with a read-only rootfs preventing attacks where the application code can be tampered with.
* Apps can only connect to addons like databases, LDAP, email relay using authentication.
* Apps are run with an AppArmor profile that disables many system calls and restricts access to `proc`
and `sys` filesystems.
* Most apps are run as non-root user. In the future, we intend to implement user namespaces.
* Each app is run in it's own subdomain as opposed to sub-paths. This ensures that XSS vulnerabilities
in one app doesn't [compromise](https://security.stackexchange.com/questions/24155/preventing-insecure-webapp-on-subdomain-compromise-security-of-main-webapp) other apps.
## Email
* Cloudron checks against the [Zen Spamhaus DNSBL](https://www.spamhaus.org/zen/) before accepting mail.
* Email can only be accessed with IMAP over TLS (IMAPS).
* Email can only be relayed (including same-domain emails) by authenticated users using SMTP/STARTTLS.
* Cloudron ensures that `MAIL FROM` is the same as the authenticated user. Users cannot spoof each other.
* All outbound mails from Cloudron are `DKIM` signed.
* Cloudron automatically sets up SPF, DMARC policies in the DNS for best email delivery.
* All incoming mail is scanned via `Spamassasin`.
## Firewall
* Cloudron blocks all incoming ports except 22 (ssh), 80 (http), 443 (https)
* When email is enabled, Cloudron allows 25 (SMTP), 587 (MSA), 993 (IMAPS) and 4190 (WebSieve)
## OS Updates
* Ubuntu [automatic security updates](https://help.ubuntu.com/community/AutomaticSecurityUpdates) are enabled
## Rate limits
The goal of rate limits is to prevent password brute force attacks.
* Cloudron password verification routes - 10 requests per second per IP.
* HTTP and HTTPS requests - 5000 requests per second per IP.
* SSH access - 5 connections per 10 seconds per IP.
* Email access (Port 25, 587, 993, 4190) - 50 connections per second per IP/App.
* Database addons access - 5000 connections per second per app (addons use 128 byte passwords).
* Email relay access - 500 connections per second per app.
* Email receive access - 50 connections per second per app.
* Auth addon access - 500 connections per second per app.
## Password restrictions
* Cloudron requires user passwords to have 1 uppercase, 1 number and 1 symbol.
* Minimum length for user passwords is 8
## Privacy
* Cloudron apps have a default `Referrer-Policy` of `no-referrer-when-downgrade`.
* Backups are optionally encrypted with AES-256-CBC.
* Let's Encrypt [submits](https://letsencrypt.org/certificates/)
all certificates to [Certificate Transparency Logs](https://www.certificate-transparency.org/).
This means that the apps that you install and use are going to be guessable. For example,
[crt.sh](https://crt.sh) can display all your subdomains and you can visit those subdomains and
guess the app. Generally, this is not a problem because using hidden DNS names is not a security
measure. If you want to avoid this, you can always use a wildcard certificate.
* Cloudron does not collect any user information and this is not our business model. We collect
information regarding the configured backend types. This helps us focus on improving backends
based on their use. You can review the specific code [here](https://git.cloudron.io/cloudron/box/blob/master/src/appstore.js#L124).
# Data directory
If you are installing a brand new Cloudron, you can configure the data directory
that Cloudron uses by passing the `--data-dir` option to `cloudron-setup`.
Note: data directory must be an `ext4` filesystem.
```
./cloudron-setup --provider <digitalocean|ec2|generic|scaleway> --data-dir /var/cloudrondata
```
If you have an existing Cloudron, we recommend moving the existing data directory
to a new location as follows (`DATA_DIR` is the location to move your data):
```
systemctl stop cloudron.target
systemctl stop docker
DATA_DIR="/var/data"
mkdir -p "${DATA_DIR}"
mv /home/yellowtent/appsdata "${DATA_DIR}"
ln -s "${DATA_DIR}/appsdata" /home/yellowtent/appsdata
mv /home/yellowtent/platformdata "${DATA_DIR}"
ln -s "${DATA_DIR}/platformdata" /home/yellowtent/platformdata
systemctl start docker
systemctl start cloudron.target
```
# Debug
You can SSH into your Cloudron and collect logs:
* `journalctl -a -u box` to get debug output of box related code.
* `docker ps` will give you the list of containers. The addon containers are named as `mail`, `postgresql`,
`mysql` etc. If you want to get a specific container's log output, `journalctl -a CONTAINER_ID=<container_id>`.
# Alerts
The Cloudron will notify the Cloudron administrator via email if apps go down, run out of memory, have updates
available etc.
You will have to setup a 3rd party service like [Cloud Watch](https://aws.amazon.com/cloudwatch/) or [UptimeRobot](http://uptimerobot.com/) to monitor the Cloudron itself. You can use `https://my.<domain>/api/v1/cloudron/status`
as the health check URL.
# Help
If you run into any problems, join us at our [chat](https://chat.cloudron.io) or [email us](mailto:support@cloudron.io).
-366
View File
@@ -1,366 +0,0 @@
# Introduction
The Cloudron is the best platform self-hosting web applications on your server. You
can easily install apps on it, add users, manage access restriction and keep your
server and apps updated with no effort.
You might wonder that there are so many 1-click app solutions out there and what is so special
about Cloudron? As the name implies, 1-click installers simply install code into a server
and leave it at that. There's so much more to do:
1. Configure a domain to point to your server
2. Setup SSL certificates and renew them periodically
3. Ensure apps are backed up correctly
4. Ensure apps are uptodate and secure
5. Have a mechanism to quickly restore apps from a backup
6. Manage users across all your apps
7. Get alerts and notifications about the status of apps
... and so on ...
We made the Cloudron to dramatically lower the bar for people to run apps on servers. Just provide
a domain name, install apps and add users. All the server management tasks listed above is
completely automated.
If you want to learn more about the secret sauce that makes the Cloudron, please read our
[architecture overview](/references/architecture.html).
# Use cases
Here are some of the apps you can run on a Cloudron:
* RSS Reader
* Chat, IRC, Jabber servers
* Public forum
* Blog
* File syncing and sharing
* Code hosting
* Email
Our list of apps is growing everyday, so be sure to [follow us on twitter](https://twitter.com/cloudron_io).
# Activation
When you first create the Cloudron, the setup wizard will ask you to setup an administrator
account. Don't worry, a Cloudron adminstrator doesn't need to know anything about maintaining
a server! It's the whole reason why we made the Cloudron. Being a Cloudron administrator is
more analagous to being the owner of a smartphone. You can always add more administrators to
the Cloudron from the `Users` menu item.
<img src="/docs/img/webadmin_domain.png" class="shadow">
The Cloudron administration page is located at the `my` subdomain. You might want to bookmark
this link!
# Apps
## Installation
You can install apps on the Cloudron by choosing the `App Store` menu item. Use the 'Search' bar
to search for apps.
Clicking on app gives you information about the app.
<img src="/docs/img/app_info.png" class="shadow">
Clicking the `Install` button will show an install dialog like below:
<img src="/docs/img/app_install.png" class="shadow">
The `Location` field is the subdomain in which your app will be installed. For example, if you use the
`mail` location for your web mail client, then it will be accessible at `mail.<domain>`.
Tip: You can access the apps directly on your browser using `mail.<domain>`. You don't have to
visit the Cloudron administration panel.
`Access control` specifies who can access this app.
* `Every Cloudron user` - Any user in your Cloudron can access the app. Initially, you are the only
user in your Cloudron. Unless you explicitly invite others, nobody else can access these apps.
Note that the term 'access' depends on the app. For a blog, this means that nobody can post new
blog posts (but anybody can view them). For a chat server, this might mean that nobody can access
your chat server.
* `Restrict to groups` - Only users in the groups can access the app.
## Updates
All your apps automatically update as and when the application author releases an update. The Cloudron
will attempt to update around midnight of your timezone.
Some app updates are not automatic. This can happen if a new version of the app has removed some features
that you were relying on. In such a case, the update has to be manually approved. This is simply a matter
of clicking the `Update` button (the green star) after you read about the changes.
<img src="/docs/img/app_update.png" class="shadow">
## Backups
<i>If you self-host, please refer to the [self-hosting documentation](/references/selfhosting.html#backups) for backups.</i>
All apps are automatically backed up every day. Backups are stored encrypted in Amazon S3. You don't have
to do anything about it. The [Cloudron CLI](https://git.cloudron.io/cloudron/cloudron-cli) tool can be used
to download application backups.
## Configuration
Apps can be reconfigured using the `Configure` button.
<img src="/docs/img/app_configure_button.png" class="shadow">
Click on the wrench button will bring up the configure dialog.
<img src="/docs/img/app_configure.png" class="shadow">
You can do the following:
* Change the location to move the app to another subdomain. Say, you want to move your blog from `blog` to `about`.
* Change who can access the app.
Changing an app's configuration has a small downtime (usually around a minute).
## Restore
Apps can be restored to a previous backup by clicking on the `Restore` button.
<img src="/docs/img/app_restore_button.png" class="shadow">
Note that restoring previous data might also restore the previous version of the software. For example, you might
be currently using Version 5 of the app. If you restore to a backup that was made with Version 3 of the app, then the restore
operation will install Version 3 of the app. This is because the latest version may not be able to handle old data.
## Uninstall
You can uninstall an app by clicking the `Uninstall` button.
<img src="/docs/img/app_uninstall_button.png" class="shadow">
Note that all data associated with the app will be immediately removed from the Cloudron. App data might still
persist in your old backups and the [CLI tool](https://git.cloudron.io/cloudron/cloudron-cli) provides a way to
restore from those old backups should it be required.
## Embedding Apps
It is possible to embed Cloudron apps into other websites. By default, this is disabled to prevent
[Clickjacking](https://cloudron.io/blog/2016-07-15-site-embedding.html).
You can set a website that is allowed to embed your Cloudron app using the app's [Configure dialog](#configuration).
Click on 'Show Advanced Settings...' and enter the embedder website name.
# Custom domain
When you create a Cloudron from cloudron.io, we provide a subdomain under `cloudron.me` like `girish.cloudron.me`.
Apps are available under that subdomain using a hyphenated name like `blog-girish.cloudron.me`.
Domain names are a thing of pride and the Cloudron makes it easy to make your apps accessible from memorable locations like `blog.girish.in`.
## Single app on a custom domain
This approach is applicable if you desire that only a single app be accessing from a custom
domain. For this, open the app's configure dialog and choose `External Domain` in the location dropdown.
<img src="/docs/img/app_external_domain.png" class="shadow">
This dialog will suggest you to add a `CNAME` record (for subdomains) or an `A` record (for naked domains).
Once you setup a record with your DNS provider, the app will be accessible from that external domain.
## Entire Cloudron on a custom domain
This approach is applicable if you want all your apps to be accessible from subdomains of your custom domain.
For example, `blog.girish.in`, `notes.girish.in`, `owncloud.girish.in`, `mail.girish.in` and so on. This
approach is also the only way that the Cloudron supports for sending and receiving emails from your domain.
For this, go to the 'Domains & Certs' menu item.
<img src="/docs/img/custom_domain_menu.png" class="shadow">
Change the domain name to your custom domain. Currently, we require that your domain be hosted on AWS Route53.
<img src="/docs/img/custom_domain_change.png" class="shadow">
Moving to a custom domain will retain all your apps and data and will take around 15 minutes. If you require assistance with another provider,
<a href="mailto:support@cloudron.io">just let us know</a>.
# User management
## Users
You can invite new users (friends, family, colleagues) with their email address from the `Users` menu. They will
receive an invite to sign up with your Cloudron. They can now access the apps that you have given them access
to.
<img src="/docs/img/users.png" class="shadow">
To remove a user, simply remove them from the list. Note that the removed user cannot access any app anymore.
## Administrators
A Cloudron administrator is a special right given to an existing Cloudron user allowing them to manage
apps and users. To make an existing user an administator, click the edit (pencil) button corresponding to
the user and check the `Allow this user to manage apps, groups and other users` checkbox.
<img src="/docs/img/administrator.png" class="shadow">
## Groups
Groups provide a convenient way to group users. It's purpose is two-fold:
* You can assign one or more groups to apps to restrict who can access for an app.
* Each group is a mailing list (forwarding address) constituting of it's members.
You can create a group by using the `Groups` menu item.
<img src="/docs/img/groups.png" class="shadow">
To set the access restriction use the app's configure dialog.
<img src="/docs/img/app_access_control.png" class="shadow">
You can now send mails to `groupname@<domain>` to address all the group members.
# Login
## Cloudron admin
The Cloudron admin page is always located at the `my` subdomain of your Cloudron domain. For custom domains,
this will be like `my.girish.in`. For domains from cloudron.io, this will be like `my-girish.cloudron.me`.
## Apps (single sign-on)
An important feature of the Cloudron is Single Sign-On. You use the same username & password for logging in
to all your apps. No more having to manage separate set of credentials for each service!
## Single user apps
Some apps only work with a single user. For example, a notes app might allow only a single user to login and add
notes. For such apps, you will be prompted during installation to select the single user who can access the app.
<img src="/docs/img/app_single_user.png" class="shadow">
If you want multiple users to use the app independently, simply install the app multiple times to different locations.
# Email
The Cloudron has a built-in email server. The primary email address is the same as the username. Emails can be sent
and received from `<username>@<domain>`. The Cloudron does not allow masquerading - one user cannot send email
pretending to be another user.
## Enabling Email
By default, Cloudron's email server only allows apps to send email. To enable users to send and receive email,
turn on the option under `Settings`. Turning on this option also allows apps to _receive_ email.
Once email is enabled, the Cloudron will keep the the `MX` DNS record updated.
<img src="/docs/img/enable_email.png" class="shadow">
## Receiving email using IMAP
Use the following settings to receive email.
* Server Name - Use the `my` subdomain of your Cloudron
* Port - 993
* Connection Security - TLS
* Username/password - Same as your Cloudron credentials
## Sending email using SMTP
Use the following settings to send email.
* Server Name - Use the `my` subdomain of your Cloudron
* Port - 587
* Connection Security - STARTTLS
* Username/password - Same as your Cloudron credentials
## Email filters using Sieve
Use the following settings to setup email filtering users via Manage Sieve.
* Server Name - Use the `my` subdomain of your Cloudron
* Port - 4190
* Connection Security - TLS
* Username/password - Same as your Cloudron credentials
The [Rainloop](https://cloudron.io/appstore.html?app=net.rainloop.cloudronapp) and [Roundcube](https://cloudron.io/appstore.html?app=net.roundcube.cloudronapp)
apps are already pre-configured to use the above settings.
## Aliases
You can configure one or more aliases alongside the primary email address of each user. You can set aliases by editing the
user's settings, available behind the edit button in the user listing. Note that aliases cannot conflict with existing user names.
<img src="/docs/img/email_alias.png" class="shadow">
Currently, it is not possible to login using the alias for SMTP/IMAP/Sieve services. Instead, add the alias as an identity in
your mail client but login using the Cloudron credentials.
## Subaddresses
Emails addressed to `<username>+tag@<domain>` will be delivered to the `username` mailbox. You can use this feature to give out emails of the form
`username+kayak@<domain>`, `username+aws@<domain>` and so on and have them all delivered to your mailbox.
## Forwarding addresses
Each group on the Cloudron is also a forwarding address. Mails can be addressed to `group@<domain>` and the mail will
be sent to each user who is part of the group.
## Marking Spam
The spam detection agent on the Cloudron requires training to identify spam. To do this, simply move your junk mails
to a pre-created folder named `Spam`. Most mail clients have a Junk or Spam button which does this automatically.
# Graphs
The Graphs view shows an overview of the disk and memory usage on your Cloudron.
<img src="/docs/img/graphs.png" class="shadow">
The `Disk Usage` graph shows you how much disk space you have left. Note that the Cloudron will
send the Cloudron admins an email notification when the disk is ~90% full.
The `Apps` Memory graph shows the memory consumed by each installed app. You can click on each segment
on the graph to see the memory consumption over time in the chart below it.
The `System` Memory graph shows the overall memory consumption on the entire Cloudron. If you see
the Free memory < 50MB frequently, you should consider upgrading to a Cloudron with more memory.
# Activity log
The `Activity` view shows the activity on your Cloudron. It includes information about who is using
the apps on your Cloudron and also tracks configuration changes.
<img src="/docs/img/activity.png" class="shadow">
# API Access
All the operations listed in this manual like installing app, configuring users and groups, are
completely programmable with a [REST API](/references/api.html).
# Domains and SSL Certificates
All apps on the Cloudron can only be reached by `https`. The Cloudron automatically installs and
renews certificates for your apps as needed. Should installation of certificate fail for reasons
beyond it's control, Cloudron admins will get a notification about it.
# OAuth Provider
Cloudron is an OAuth 2.0 provider. To integrate Cloudron login into an external application, create
an OAuth application under `API Access`.
You can use the following OAuth URLs to add Cloudron in the external app:
```
authorizationURL: https://my.<domain>/api/v1/oauth/dialog/authorize
tokenURL: https://my.<domain>/api/v1/oauth/token
```
# Moving to a larger Cloudron
When using a Cloudron from cloudron.io, it is easy to migrate your apps and data to a bigger server.
In the `Settings` page, you can change the plan.
<insert picture>
# Command line tool
If you are a software developer or a sysadmin, the Cloudron comes with a CLI tool that can be
used to develop custom apps for the Cloudron. Read more about it [here](https://git.cloudron.io/cloudron/cloudron-cli).
-623
View File
@@ -1,623 +0,0 @@
# Overview
This tutorial provides an introduction to developing applications
for the Cloudron using node.js.
# Installation
## Install CLI tool
The Cloudron CLI tool allows you to install, configure and test apps on your Cloudron.
Installing the CLI tool requires [node.js](https://nodejs.org/) and
[npm](https://www.npmjs.com/). You can then install the CLI tool using the following
command:
```
sudo npm install -g cloudron
```
Note: Depending on your setup, you can run the above command without `sudo`.
## Testing your installation
The `cloudron` command should now be available in your path.
Let's login to the Cloudron as follows:
```
$ cloudron login
Cloudron Hostname: craft.selfhost.io
Enter credentials for craft.selfhost.io:
Username: girish
Password:
Login successful.
```
## Your First Application
Creating an application for Cloudron can be summarized as follows:
1. Create a web application using any language/framework. This web application must run a HTTP server
and can optionally provide other services using custom protocols (like git, ssh, TCP etc).
2. Create a [Dockerfile](http://docs.docker.com/engine/reference/builder/) that specifies how to create
an application ```image```. An ```image``` is essentially a bundle of the application source code
and it's dependencies.
3. Create a [CloudronManifest.json](/references/manifest.html) file that provides essential information
about the app. This includes information required for the Cloudron Store like title, version, icon and
runtime requirements like `addons`.
## Simple Web application
To keep things simple, we will start by deploying a trivial node.js server running on port 8000.
Create a new project folder `tutorial/` and add a file named `tutorial/server.js` with the following content:
```javascript
var http = require("http");
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello World\n");
});
server.listen(8000);
console.log("Server running at port 8000");
```
## Dockerfile
A Dockerfile contains commands to assemble an image.
Create a file named `tutorial/Dockerfile` with the following content:
```dockerfile
FROM cloudron/base:0.10.0
ADD server.js /app/code/server.js
CMD [ "/usr/local/node-6.9.5/bin/node", "/app/code/server.js" ]
```
The `FROM` command specifies that we want to start off with Cloudron's [base image](/references/baseimage.html).
All Cloudron apps **must** start from this base image.
The `ADD` command copies the source code of the app into the directory `/app/code`.
While this example only copies a single file, the ADD command can be used to copy directory trees as well.
See the [Dockerfile](https://docs.docker.com/reference/builder/#add) documentation for more details.
The `CMD` command specifies how to run the server. There are multiple versions of node available under `/usr/local`. We
choose node v6.9.5 for our app.
## CloudronManifest.json
The `CloudronManifest.json` specifies
* Information about displaying the app on the Cloudron Store. For example,
the title, author information, description etc
* Information for installing the app on the Cloudron. This includes fields
like httpPort, tcpPorts.
Create the CloudronManifest.json using the following command:
```
$ cloudron init
id: io.cloudron.tutorial # unique id for this app. use reverse domain name convention
author: John Doe # developer or company name of the for user <email>
title: Tutorial App # Cloudron Store title of this app
description: App that uses node.js # A string or local file reference like file://DESCRIPTION.md
tagline: Changing the world one app at a time # A tag line for this app for the Cloudron Store
website: https://cloudron.io # A link to this app's website
contactEmail: support@cloudron.io # Contact email of developer or company
httPort: 8000 # The http port on which this application listens to
```
The above command creates a CloudronManifest.json:
File ```tutorial/CloudronManifest.json```
```json
{
"id": "io.cloudron.tutorial",
"author": "John Doe",
"title": "Tutorial App",
"description": "App that uses node.js",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {}
},
"minBoxVersion": "0.0.1",
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"mediaLinks": []
}
```
You can read in more detail about each field in the [Manifest reference](/references/manifest.html).
# Installing
## Building
We now have all the necessary files in place to build and deploy the app to the Cloudron.
Building creates an image of the app using the Dockerfile which can then be used to deploy
to the Cloudron.
Building, pushing and pulling docker images is very bandwidth and CPU intensive. To alleviate this
problem, apps are built using the `build service` which uses `cloudron.io` account credentials.
**Warning**: As of this writing, the build service uses the public Docker registry and the images that are built
can be downloaded by anyone. This means that your source code will be viewable by others.
Initiate a build using ```cloudron build```:
```
$ cloudron build
Building io.cloudron.tutorial@0.0.1
Appstore login:
Email: ramakrishnan.girish@gmail.com # cloudron.io account
Password: # Enter password
Login successful.
Build scheduled with id 76cebfdd-7822-4f3d-af17-b3eb393ae604
Downloading source
Building
Step 0 : FROM cloudron/base:0.10.0
---> 97583855cc0c
Step 1 : ADD server.js /app/code
---> b09b97ecdfbc
Removing intermediate container 03c1e1f77acb
Step 2 : CMD /usr/local/node-6.9.5/bin/node /app/code/main.js
---> Running in 370f59d87ab2
---> 53b51eabcb89
Removing intermediate container 370f59d87ab2
Successfully built 53b51eabcb89
The push refers to a repository [cloudron/img-2074d69134a7e0da3d6cdf3c53e241c4] (len: 1)
Sending image list
Pushing repository cloudron/img-2074d69134a7e0da3d6cdf3c53e241c4 (1 tags)
Image already pushed, skipping 57f52d167bbb
Image successfully pushed b09b97ecdfbc
Image successfully pushed 53b51eabcb89
Pushing tag for rev [53b51eabcb89] on {https://cdn-registry-1.docker.io/v1/repositories/cloudron/img-2074d69134a7e0da3d6cdf3c53e241c4/tags/76cebfdd-7822-4f3d-af17-b3eb393ae604}
Build succeeded
```
## Installing
Now that we have built the image, we can install our latest build on the Cloudron
using the following command:
```
$ cloudron install
Using cloudron craft.selfhost.io
Using build 76cebfdd-7822-4f3d-af17-b3eb393ae604 from 1 hour ago
Location: tutorial # This is the location into which the application installs
App is being installed with id: 4dedd3bb-4bae-41ef-9f32-7f938995f85e
=> Waiting to start installation
=> Registering subdomain .
=> Verifying manifest .
=> Downloading image ..............
=> Creating volume .
=> Creating container
=> Setting up collectd profile ................
=> Waiting for DNS propagation ...
App is installed.
```
This makes the app available at https://tutorial-craft.selfhost.io.
Open the app in your default browser:
```
cloudron open
```
You should see `Hello World`.
# Testing
The application testing cycle involves `cloudron build` and `cloudron install`.
Note that `cloudron install` updates an existing app in place.
You can view the logs using `cloudron logs`. When the app is running you can follow the logs
using `cloudron logs -f`.
For example, you can see the console.log output in our server.js with the command below:
```
$ cloudron logs
Using cloudron craft.selfhost.io
2015-05-08T03:28:40.233940616Z Server running at port 8000
```
It is also possible to run a *shell* and *execute* arbitrary commands in the context of the application
process by using `cloudron exec`. By default, exec simply drops you into an interactive bash shell with
which you can inspect the file system and the environment.
```
$ cloudron exec
```
You can also execute arbitrary commands:
```
$ cloudron exec env # display the env variables that your app is running with
```
# Storing data
For file system storage, an app can use the `localstorage` addon to store data under `/app/data`.
When the `localstorage` addon is active, any data under /app/data is automatically backed up. When an
app is updated, /app/data already contains the data generated by the previous version.
*Note*: For convenience, the initial CloudronManifest.json generated by `cloudron init` already contains this
addon.
Let us put this theory into action by saving a *visit counter* as a file.
*server.js* has been modified to count the number of visitors on the site by storing a counter
in a file named ```counter.dat```.
File ```tutorial/server.js```
```javascript
var http = require('http'),
fs = require('fs'),
util = require('util');
var COUNTER_FILE = '/app/data/counter.dat';
var server = http.createServer(function (request, response) {
var counter = 0;
if (fs.existsSync(COUNTER_FILE)) {
// read existing counter if it exists
counter = parseInt(fs.readFileSync(COUNTER_FILE, 'utf8'), 10);
}
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(util.format("Hello World. %s visitors have visited this page\n", counter));
++counter; // bump the counter
fs.writeFileSync(COUNTER_FILE, counter + '', 'utf8'); // save back counter
});
server.listen(8000);
console.log("Server running at port 8000");
```
Now every time you refresh the page you will notice that the counter bumps up. You will
also notice that if you make changes to the app and do a `cloudron install`, the `counter.dat`
is *retained* across updates.
# Database
Most web applications require a database of some form. In theory, it is possible to run any
database you want as part of the application image. This is, however, a waste of server resources
should every app runs it's own database server.
To solve this, the Cloudron provides shareable resources like databases in form of ```addons```.
The database server is managed by the Cloudron and the application simply needs to request access to
the database in the CloudronManifest.json. While the database server itself is a shared resource, the
databases are exclusive to the application. Each database is password protected and accessible only
to the application. Databases and tables can be configured without restriction as the application
requires.
Cloudron currently provides `mysql`, `postgresql`, `mongodb`, `redis` database addons.
For this tutorial, let us try to save the counter in `redis` addon. For this, we make use of the
[redis](https://www.npmjs.com/package/redis) module.
Since this is a node.js app, let's add a very basic `package.json` containing the `redis` module dependency.
File `tutorial/package.json`
```json
{
"name": "tutorial",
"version": "1.0.0",
"dependencies": {
"redis": "^0.12.1"
}
}
```
and modify our Dockerfile to look like this:
File `tutorial/Dockerfile`
```dockerfile
FROM cloudron/base:0.10.0
ENV PATH /usr/local/node-6.9.5/bin:$PATH
ADD server.js /app/code/server.js
ADD package.json /app/code/package.json
WORKDIR /app/code
RUN npm install --production
CMD [ "node", "/app/code/server.js" ]
```
Notice the new `RUN` command which installs the node module dependencies in package.json using `npm install`.
Since we want to use redis, we have to modify the CloudronManifest.json to make redis available for this app.
File `tutorial/CloudronManifest.json`
```json
{
"id": "io.cloudron.tutorial",
"author": "John Doe",
"title": "Tutorial App",
"description": "App that uses node.js",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {},
"redis": {}
},
"minBoxVersion": "0.0.1",
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"mediaLinks": []
}
```
When the application runs, environment variables `REDIS_HOST`, `REDIS_PORT` and
`REDIS_PASSWORD` are injected. You can read about the environment variables in the
[Redis reference](/references/addons.html#redis).
Let's change `server.js` to use redis instead of file backed counting:
File ```tutorial/server.js```
```javascript
var http = require('http'),
fs = require('fs'),
util = require('util'),
redis = require('redis');
var redisClient = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
redisClient.auth(process.env.REDIS_PASSWORD);
redisClient.on("error", function (err) {
console.log("Redis Client Error " + err);
});
var COUNTER_KEY = 'counter';
var server = http.createServer(function (request, response) {
redisClient.get(COUNTER_KEY, function (err, reply) {
var counter = (!err && reply) ? parseInt(reply, 10) : 0;
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(util.format("Hello World. %s visitors have visited this page\n", counter));
redisClient.incr(COUNTER_KEY);
});
});
server.listen(8000);
console.log("Server running at port 8000");
```
Simply `cloudron build` and `cloudron install` to test your app!
# Authentication
The Cloudron has a centralized panel for managing users and groups. Apps can integrate Single Sign-On
authentication using LDAP or OAuth.
Note that apps that are single user can skip Single Sign-On support. The Cloudron implements an `OAuth
proxy` (accessed through the app configuration dialog) that optionally lets the Cloudron admin make the
app visible only for logged in users.
## LDAP
Let's start out by adding the [ldap](/references/addons.html#ldap) addon to the manifest.
File `tutorial/CloudronManifest.json`
```json
{
"id": "io.cloudron.tutorial",
"author": "John Doe",
"title": "Tutorial App",
"description": "App that uses node.js",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {},
"ldap": {}
},
"minBoxVersion": "0.0.1",
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"mediaLinks": []
}
```
Building and installing the app shows that the app gets new LDAP specific environment variables.
```
$ cloudron build
$ cloudron install
$ cloudron exec env | grep LDAP
LDAP_SERVER=172.17.42.1
LDAP_PORT=3002
LDAP_URL=ldap://172.17.42.1:3002
LDAP_USERS_BASE_DN=ou=users,dc=cloudron
LDAP_GROUPS_BASE_DN=ou=groups,dc=cloudron
```
Let's test the environment variables to use by using the [ldapjs](http://www.ldapjs.org) npm module.
We start by adding ldapjs to package.json.
File `tutorial/package.json`
```json
{
"name": "tutorial",
"version": "1.0.0",
"dependencies": {
"ldapjs": "^0.7.1"
}
}
```
The server code has been modified to authenticate using the `X-Username` and `X-Password` headers for
any path other than '/'.
File `tutorial/server.js`
```javascript
var http = require("http"),
ldap = require('ldapjs');
var ldapClient = ldap.createClient({ url: process.env.LDAP_URL });
var server = http.createServer(function (request, response) {
if (request.url === '/') {
response.writeHead(200, {"Content-Type": "text/plain"});
return response.end();
}
var username = request.headers['x-username'] || '';
var password = request.headers['x-password'] || '';
var ldapDn = 'cn=' + username + ',' + process.env.LDAP_USERS_BASE_DN;
ldapClient.bind(ldapDn, password, function (error) {
if (error) {
response.writeHead(401, {"Content-Type": "text/plain"});
response.end('Failed to authenticate: ' + error);
} else {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end('Successfully authenticated');
}
});
});
server.listen(8000);
console.log("Server running at port 8000");
```
Once we have used `cloudron build` and `cloudron install`, you can use `curl` to test
credentials as follows:
```bash
# Test with various credentials here. Your cloudon admin username and password should succeed.
curl -X 'X-Username: admin' -X 'X-Password: pass' https://tutorial-craft.selfhost.io/login
```
## OAuth
An app can integrate with OAuth 2.0 Authorization code grant flow by adding
[oauth](/references/addons.html#oauth) to CloudronManifest.json `addons` section.
Doing so will get the following environment variables:
```
$ cloudron exec env
OAUTH_CLIENT_ID=cid-addon-4089f65a-2adb-49d2-a6d1-e519b7d85e8d
OAUTH_CLIENT_SECRET=5af99a9633283aa15f5e6df4a108ff57f82064e4845de8bce8ad3af54dfa9dda
OAUTH_ORIGIN=https://my-craft.selfhost.io
API_ORIGIN=https://my-craft.selfhost.io
HOSTNAME=tutorial-craft.selfhost.io
```
OAuth Authorization code grant flow works as follows:
* App starts the flow by redirecting the user to Cloudron authorization endpoint of the following format:
```
https://API_ORIGIN/api/v1/oauth/dialog/authorize?response_type=code&client_id=OAUTH_CLIENT_ID&redirect_uri=CALLBACK_URL&scope=profile
```
In the above URL, API_ORIGIN and OAUTH_CLIENT_ID are environment variables. CALLBACK_URL is a url of the app
to which the user will be redirected back to after successful authentication. CALLBACK_URL has to have the
same origin as the app.
* The Cloudron OAuth server authenticates the user (using a password form) at the above URL. It also establishes
that the user grants the client's access request.
* If the user authenticated successfully, it will redirect the browser to CALLBACK_URL with a `code` query parameter.
* The app can exchange the `code` above for a `access token` by using the `OAUTH_CLIENT_SECRET`. It does so by making
a _POST_ request to the following url:
```
https://API_ORIGIN/api/v1/oauth/token?response_type=token&client_id=OAUTH_CLIENT_ID
```
with the following request body (json):
```json
{
"grant_type": "authorization_code",
"code": "<the code received in CALLBACK_URL query parameter>",
"redirect_uri": "https://<HOSTNAME>",
"client_id": "<OAUTH_CLIENT_ID>",
"client_secret": "<OAUTH_CLIENT_SECRET>"
}
```
In the above URL, API_ORIGIN, OAUTH_CLIENT_ID and HOSTNAME are environment variables. The response contains
the `access_token` in the body.
* The `access_token` can be used to get the [user's profile](/references/api.html#profile) using the following url:
```
https://API_ORIGIN/api/v1/profile?access_token=ACCESS_TOKEN
```
The `access_token` may also be provided in the `Authorization` header as `Bearer: <token>`.
An implementation of the above OAuth logic is at [ircd-app](https://github.com/cloudron-io/ircd-app/blob/master/settings/app.js).
The following libraries implement Cloudron OAuth for Ruby and Javascript.
* [omniauth-cloudron](https://github.com/cloudron-io/omniauth-cloudron)
* [passport-cloudron](https://github.com/cloudron-io/passport-cloudron)
# Beta Testing
Once your app is ready, you can upload it to the store for `beta testing` by
other Cloudron users. This can be done using:
```
cloudron appstore upload
```
The app should now be visible in the Store view of your cloudron under
the 'Testing' section. You can check if the icon, description and other details
appear correctly.
Other Cloudron users can install your app on their Cloudron's using
`cloudron install --appstore-id <appid@version>`. Note that this currently
requires your beta testers to install the CLI tool and put their Cloudron in
developer mode.
# Publishing
Once you are satisfied with the beta testing, you can submit it for review.
```
cloudron appstore submit
```
The cloudron.io team will review the app and publish the app to the store.
# Next steps
Congratulations! You are now well equipped to build web applications for the Cloudron.
# Samples
* [Lets Chat](https://github.com/cloudron-io/letschat-app)
* [Haste bin](https://github.com/cloudron-io/haste-app)
* [Pasteboard](https://github.com/cloudron-io/pasteboard-app)
-719
View File
@@ -1,719 +0,0 @@
# Overview
This tutorial outlines how to package an existing web application for the Cloudron.
If you are aware of Docker and Heroku, you should feel at home packaging for the
Cloudron. Roughly, the steps involved are:
* Create a Dockerfile for your application. If your application already has a Dockerfile, it
is a good starting point for packaging for the Cloudron. By virtue of Docker, the Cloudron
is able to run apps written in any language/framework.
* Create a CloudronManifest.json that provides information like title, author, description
etc. You can also specify the addons (like database) required
to run your app. When the app runs on the Cloudron, it will have environment
variables set for connecting to the addon.
* Test the app on your Cloudron with the CLI tool.
* Optionally, submit the app to [Cloudron Store](/appstore.html).
# Prerequisites
## Install CLI tool
The Cloudron CLI tool allows you to install, configure and test apps on your Cloudron.
Installing the CLI tool requires [node.js](https://nodejs.org/) and
[npm](https://www.npmjs.com/). You can then install the CLI tool using the following
command:
```
sudo npm install -g cloudron
```
Note: Depending on your setup, you can run the above command without `sudo`.
## Login to Cloudron
The `cloudron` command should now be available in your path.
You can login to your Cloudron now:
```
$ cloudron login
Cloudron Hostname: craft.selfhost.io
Enter credentials for craft.selfhost.io:
Username: girish
Password:
Login successful.
```
# Basic app
We will first package a very simple app to understand how the packaging works.
You can clone this app from https://git.cloudron.io/cloudron/tutorial-basic.
## The server
The basic app server is a very simple HTTP server that runs on port 8000.
While the server in this tutorial uses node.js, you can write your server
in any language you want.
```server.js
var http = require("http");
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello World\n");
});
server.listen(8000);
console.log("Server running at port 8000");
```
## Dockerfile
The Dockerfile contains instructions on how to create an image for your application.
```Dockerfile
FROM cloudron/base:0.10.0
ADD server.js /app/code/server.js
CMD [ "/usr/local/node-4.7.3/bin/node", "/app/code/server.js" ]
```
The `FROM` command specifies that we want to start off with Cloudron's [base image](/references/baseimage.html).
All Cloudron apps **must** start from this base image. This approach conserves space on the Cloudron since
Docker images tend to be quite large and also helps us to do a security audit on apps more easily.
The `ADD` command copies the source code of the app into the directory `/app/code`. There is nothing special
about the `/app/code` directory and it is merely a convention we use to store the application code.
The `CMD` command specifies how to run the server. The base image already contains many different versions of
node.js. We use Node 4.7.3 here.
This Dockerfile can be built and run locally as:
```
docker build -t tutorial .
docker run -p 8000:8000 -t tutorial
```
## Manifest
The `CloudronManifest.json` specifies
* Information for installing and running the app on the Cloudron. This includes fields like addons, httpPort, tcpPorts.
* Information about displaying the app on the Cloudron Store. For example, fields like title, author, description.
Create the CloudronManifest.json using `cloudron init` as follows:
```
$ cloudron init
id: io.cloudron.tutorial # unique id for this app. use reverse domain name convention
author: John Doe # developer or company name of the for user <email>
title: Tutorial App # Cloudron Store title of this app
description: App that uses node.js # A string or local file reference like file://DESCRIPTION.md
tagline: Changing the world one app at a time # A tag line for this app for the Cloudron Store
website: https://cloudron.io # A link to this app's website
contactEmail: support@cloudron.io # Contact email of developer or company
httPort: 8000 # The http port on which this application listens to
```
The above command creates a CloudronManifest.json:
File ```tutorial/CloudronManifest.json```
```json
{
"id": "io.cloudron.tutorial",
"title": "Tutorial App",
"author": "John Doe",
"description": "file://DESCRIPTION.md",
"changelog": "file://CHANGELOG",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {}
},
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"tags": [
"changme"
],
"mediaLinks": [ ]
}
```
You can read in more detail about each field in the [Manifest reference](/references/manifest.html). The
`localstorage` addon allows the app to store files in `/app/data`. We will explore addons further further
down in this tutorial.
Additional files created by `init` are:
* `DESCRIPTION.md` - A markdown file providing description of the app for the Cloudron Store.
* `CHANGELOG` - A file containing change information for each version released to the Cloudron Store. This
information is shown when the user updates the app.
# Installing
We now have all the necessary files in place to build and deploy the app to the Cloudron.
## Building
Building, pushing and pulling docker images can be very bandwidth and CPU intensive. To alleviate this
problem, apps are built using the `build service` which uses `cloudron.io` account credentials.
**Warning**: As of this writing, the build service uses the public Docker registry and the images that are built
can be downloaded by anyone. This means that your source code will be viewable by others.
Initiate a build using ```cloudron build```:
```
$ cloudron build
Building io.cloudron.tutorial@0.0.1
cloudron.io login:
Email: ramakrishnan.girish@gmail.com # cloudron.io account
Password: # Enter password
Login successful.
Build scheduled with id e7706847-f2e3-4ba2-9638-3f334a9453a5
Waiting for build to begin, this may take a bit...
Downloading source
Building
Step 1 : FROM cloudron/base:0.10.0
---> be9fc6312b2d
Step 2 : ADD server.js /app/code/server.js
---> 10513e428d7a
Removing intermediate container 574573f6ed1c
Step 3 : CMD /usr/local/node-4.2.1/bin/node /app/code/server.js
---> Running in b541d149b6b9
---> 51aa796ea6e5
Removing intermediate container b541d149b6b9
Successfully built 51aa796ea6e5
Pushing
The push refers to a repository [docker.io/cloudron/img-062037096d69bbf3ffb5b9316ad89cb9] (len: 1)
Pushed 51aa796ea6e5
Pushed 10513e428d7a
Image already exists be9fc6312b2d
Image already exists a0261a2a7c75
Image already exists f9d4f0f1eeed
Image already exists 2b650158d5d8
e7706847-f2e3-4ba2-9638-3f334a9453a5: digest: sha256:8241d68b65874496191106ecf2ee8f3df2e05a953cd90ff074a6f8815a49389c size: 26098
Build succeeded
Success
```
## Installing
Now that we have built the image, we can install our latest build on the Cloudron
using the following command:
```
$ cloudron install
Using cloudron craft.selfhost.io
Using build 76cebfdd-7822-4f3d-af17-b3eb393ae604 from 1 hour ago
Location: tutorial # This is the location into which the application installs
App is being installed with id: 4dedd3bb-4bae-41ef-9f32-7f938995f85e
=> Waiting to start installation
=> Registering subdomain .
=> Verifying manifest .
=> Downloading image ..............
=> Creating volume .
=> Creating container
=> Setting up collectd profile ................
=> Waiting for DNS propagation ...
App is installed.
```
Open the app in your default browser:
```
cloudron open
```
You should see `Hello World`.
# Testing
The application testing cycle involves `cloudron build` and `cloudron install`.
Note that `cloudron install` updates an existing app in place.
You can view the logs using `cloudron logs`. When the app is running you can follow the logs
using `cloudron logs -f`.
For example, you can see the console.log output in our server.js with the command below:
```
$ cloudron logs
Using cloudron craft.selfhost.io
16:44:11 [main] Server running at port 8000
```
It is also possible to run a *shell* and *execute* arbitrary commands in the context of the application
process by using `cloudron exec`. By default, exec simply drops you into an interactive bash shell with
which you can inspect the file system and the environment.
```
$ cloudron exec
```
You can also execute arbitrary commands:
```
$ cloudron exec env # display the env variables that your app is running with
```
### Debugging
An app can be placed in `debug` mode by passing `--debug` to `cloudron install` or `cloudron configure`.
Doing so, runs the app in a non-readonly rootfs and unlimited memory. By default, this will also ignore
the `RUN` command specified in the Dockerfile. The developer can then interactively test the app and
startup scripts using `cloudron exec`.
This mode can be used to identify the files being modified by your application - often required to
debug situations where your app does not run on a readonly rootfs. Run your app using `cloudron exec`
and use `find / -mmin -30` to find file that have been changed or created in the last 30 minutes.
You can turn off debugging mode using `cloudron configure --no-debug`.
# Addons
## Filesystem
The application container created on the Cloudron has a `readonly` file system. Writing to any location
other than the below will result in an error:
* `/tmp` - Use this location for temporary files. The Cloudron will cleanup any files in this directory
periodically.
* `/run` - Use this location for runtime configuration and dynamic data. These files should not be expected
to persist across application restarts (for example, after an update or a crash).
* `/app/data` - Use this location to store application data that is to be backed up. To use this location,
you must use the [localstorage](/references/addons.html#localstorage) addon. For convenience, the initial CloudronManifest.json generated by
`cloudron init` already contains this addon.
## Database
Most web applications require a database of some form. In theory, it is possible to run any
database you want as part of the application image. This is, however, a waste of server resources
should every app runs it's own database server.
Cloudron currently provides [mysql](/references/addons.html#mysql), [postgresql](/references/addons.html#postgresql),
[mongodb](/references/addons.html#mongodb), [redis](/references/addons.html#redis) database addons. When choosing
these addons, the Cloudron will inject environment variables that contain information on how to connect
to the addon.
See https://git.cloudron.io/cloudron/tutorial-redis for a simple example of how redis can be used by
an application. The server simply uses the environment variables to connect to redis.
## Email
Cloudron applications can send email using the `sendmail` addon. Using the `sendmail` addon provides
the SMTP server and authentication credentials in environment variables.
Cloudron applications can also receive mail via IMAP using the `recvmail` addon.
## Authentication
The Cloudron has a centralized panel for managing users and groups. Apps can integrate Single Sign-On
authentication using LDAP or OAuth.
Apps can integrate with the Cloudron authentication system using LDAP, OAuth or Simple Auth. See the
[authentication](/references/authentication.html) reference page for more details.
See https://git.cloudron.io/cloudron/tutorial-ldap for a simple example of how to authenticate via LDAP.
For apps that are single user can skip Single Sign-On support by setting the `"singleUser": true`
in the manifest. By doing so, the Cloudron will installer will show a dialog to choose a user.
For app that have no user management at all, the Cloudron implements an `OAuth proxy` that
optionally lets the Cloudron admin make the app visible only for logged in users.
# Best practices
## No Setup
A Cloudron app is meant to instantly usable after installation. For this reason, Cloudron apps must not
show any setup screen after installation and should simply choose reasonable defaults.
Databases, email configuration should be automatically picked up from the environment variables using
addons.
## Docker
Cloudron uses Docker in the backend, so the package build script is a regular `Dockerfile`.
The app is run as a read-only docker container. Only `/run` (dynamic data), `/app/data` (backup data) and `/tmp` (temporary files) are writable at runtime. Because of this:
* Install any required packages in the Dockerfile.
* Create static configuration files in the Dockerfile.
* Create symlinks to dynamic configuration files under `/run` in the Dockerfile.
### Source directory
By convention, Cloudron apps install the source code in `/app/code`. Do not forget to create the directory for the code of the app:
```sh
RUN mkdir -p /app/code
WORKDIR /app/code
```
### Download archives
When packaging an app you often want to download and extract archives (e.g. from github).
This can be done in one line by combining `wget` and `tar` like this:
```docker
ENV VERSION 1.6.2
RUN wget "https://github.com/FreshRSS/FreshRSS/archive/${VERSION}.tar.gz" -O - \
| tar -xz -C /app/code --strip-components=1
```
The `--strip-components=1` causes the topmost directory in the archive to be skipped.
Always pin the download to a specific tag or commit instead of using `HEAD` or `master`
so that the builds are reasonably reproducible.
### Applying patches
To get the app working in Cloudron, sometimes it is necessary to patch the original sources. Patch is a safe way to modify sources, as it fails when the expected original sources changed too much.
First create a backup copy of the full sources (to be able to calculate the differences):
```sh
cp -a extensions extensions-orig
```
Then modify the sources in the original path and when finished, create a patch like this:
```sh
diff -Naru extensions-orig/ extensions/ > change-ttrss-file-path.patch
```
Add and apply this patch to the sources in the Dockerfile:
```docker
ADD change-ttrss-file-path.patch /app/code/change-ttrss-file-path.patch
RUN patch -p1 -d /app/code/extensions < /app/code/change-ttrss-file-path.patch
```
The `-p1` causes patch to ignore the topmost directory in the patch.
## Process manager
Docker supports restarting processes natively. Should your application crash, it will be restarted
automatically. If your application is a single process, you do not require any process manager.
Use supervisor, pm2 or any of the other process managers if you application has more then one component.
This **excludes** web servers like apache, nginx which can already manage their children by themselves.
Be sure to pick a process manager that [forwards signals](#sigterm-handling) to child processes.
## Automatic updates
Some apps support automatic updates by overwriting themselves. A Cloudron app cannot overwrite itself
because of the read-only file system. For this reason, disable auto updates for app and let updates be
triggered through the Cloudron Store. This ties in better to the Cloudron's update and restore approach
should something go wrong with the update.
## Logging
Cloudron applications stream their logs to stdout and stderr. In practice, this ideal is hard to achieve.
Some programs like apache simply don't log to stdout. In those cases, simply log to `/tmp` or `/run`.
Logging to stdout has many advantages:
* App does not need to rotate logs and the Cloudron takes care of managing logs.
* App does not need special mechanism to release log file handles (on a log rotate).
* Integrates better with tooling like cloudron cli.
## Memory
By default, applications get 256MB RAM (including swap). This can be changed using the `memoryLimit`
field in the manifest.
Design your application runtime for concurrent use by 50 users. The Cloudron is not designed for
concurrent access by 100s or 1000s of users.
An app can determine it's memory limit by reading `/sys/fs/cgroup/memory/memory.limit_in_bytes`.
## Authentication
Apps should integrate with one of the [authentication strategies](/references/authentication.html).
This saves the user from having to manage separate set of credentials for each app.
## Start script
Many apps do not launch the server directly, as we did in our basic example. Instead, they execute
a `start.sh` script (named so by convention) which is used as the app entry point.
At the end of the Dockerfile you should add your start script (`start.sh`) and set it as the default command.
Ensure that the `start.sh` is executable in the app package repo. This can be done with `chmod +x start.sh`.
```docker
ADD start.sh /app/code/start.sh
CMD [ "/app/code/start.sh" ]
```
### One-time init
One common pattern is to initialize the data directory with some commands once depending on the existence of a special `.initialized` file.
```sh
if ! [ -f /app/data/.initialized ]; then
echo "Fresh installation, setting up data directory..."
# Setup commands here
touch /app/data/.initialized
echo "Done."
fi
```
To copy over some files from the code directory you can use the following command:
```sh
rsync -a /app/code/config/ /app/data/config/
```
### chown data files
Since the app containers use other user ids than the host, it is sometimes necessary to change the permissions on the data directory:
```sh
chown -R cloudron.cloudron /app/data
```
For Apache+PHP apps you might need to change permissions to `www-data.www-data` instead.
### Persisting random values
Some apps need a random value that is initialized once and does not change afterwards (e.g. a salt for security purposes). This can be accomplished by creating a random value and storing it in a file in the data directory like this:
```sh
if ! [ -e /app/data/.salt ]; then
dd if=/dev/urandom bs=1 count=1024 2>/dev/null | sha1sum | awk '{ print $1 }' > /app/data/.salt
fi
SALT=$(cat /app/data/.salt)
```
### Generate config
Addon information (mail, database) exposed as environment are subject to change across restarts and an application must use these values directly (i.e not cache them across restarts). For this reason, it usually regenerates any config files with the current database settings on each invocation.
First create a config file template like this:
```sh
... snipped ...
'mysql' => array(
'driver' => 'mysql',
'host' => '##MYSQL_HOST',
'port' => '##MYSQL_PORT',
'database' => '##MYSQL_DATABASE',
'username' => '##MYSQL_USERNAME',
'password' => '##MYSQL_PASSWORD',
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
'prefix' => '',
),
... snipped ...
```
Add the template file to the Dockerfile and create a symlink to the dynamic configuration file as follows:
```docker
ADD database.php.template /app/code/database.php.template
RUN ln -s /run/paperwork/database.php /app/code/database.php
```
Then in `start.sh`, generate the real config file under `/run` from the template like this:
```sh
sed -e "s/##MYSQL_HOST/${MYSQL_HOST}/" \
-e "s/##MYSQL_PORT/${MYSQL_PORT}/" \
-e "s/##MYSQL_DATABASE/${MYSQL_DATABASE}/" \
-e "s/##MYSQL_USERNAME/${MYSQL_USERNAME}/" \
-e "s/##MYSQL_PASSWORD/${MYSQL_PASSWORD}/" \
-e "s/##REDIS_HOST/${REDIS_HOST}/" \
-e "s/##REDIS_PORT/${REDIS_PORT}/" \
/app/code/database.php.template > /run/paperwork/database.php
```
### Non-root user
The cloudron runs the `start.sh` as root user. This is required for various commands like `chown` to
work as expected. However, to keep the app and cloudron secure, always run the app with the least
required permissions.
The `gosu` tool lets you run a binary with a specific user/group as follows:
```sh
/usr/local/bin/gosu cloudron:cloudron node /app/code/.build/bundle/main.js
```
### SIGTERM handling
bash, by default, does not automatically forward signals to child processes. This would mean that a SIGTERM sent to the parent processes does not reach the children. For this reason, be sure to `exec` as the
last line of the start.sh script. Programs like gosu, nginx, apache do proper SIGTERM handling.
For example, start apache using `exec` as below:
```sh
echo "Starting apache"
APACHE_CONFDIR="" source /etc/apache2/envvars
rm -f "${APACHE_PID_FILE}"
exec /usr/sbin/apache2 -DFOREGROUND
```
## Popular stacks
### Apache
Apache requires some configuration changes to work properly with Cloudron. The following commands configure Apache in the following way:
* Disable all default sites
* Print errors into the app's log and disable other logs
* Limit server processes to `5` (good default value)
* Change the port number to Cloudrons default `8000`
```docker
RUN rm /etc/apache2/sites-enabled/* \
&& sed -e 's,^ErrorLog.*,ErrorLog "/dev/stderr",' -i /etc/apache2/apache2.conf \
&& sed -e "s,MaxSpareServers[^:].*,MaxSpareServers 5," -i /etc/apache2/mods-available/mpm_prefork.conf \
&& a2disconf other-vhosts-access-log \
&& echo "Listen 8000" > /etc/apache2/ports.conf
```
Afterwards, add your site config to Apache:
```docker
ADD apache2.conf /etc/apache2/sites-available/app.conf
RUN a2ensite app
```
In `start.sh` Apache can be started using these commands:
```sh
echo "Starting apache..."
APACHE_CONFDIR="" source /etc/apache2/envvars
rm -f "${APACHE_PID_FILE}"
exec /usr/sbin/apache2 -DFOREGROUND
```
### PHP
PHP wants to store session data at `/var/lib/php/sessions` which is read-only in Cloudron. To fix this problem you can move this data to `/run/php/sessions` with these commands:
```docker
RUN rm -rf /var/lib/php/sessions && ln -s /run/php/sessions /var/lib/php/sessions
```
Don't forget to create this directory and it's ownership in the `start.sh`:
```sh
mkdir -p /run/php/sessions
chown www-data:www-data /run/php/sessions
```
### Java
Java scales its memory usage dynamically according to the available system memory. Due to how Docker works, Java sees the hosts total memory instead of the memory limit of the app. To restrict Java to the apps memory limit it is necessary to add a special parameter to Java calls.
```sh
LIMIT=$(($(cat /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes)/2**20))
export JAVA_OPTS="-XX:MaxRAM=${LIMIT}M"
java ${JAVA_OPTS} -jar ...
```
# App Store
## Requirements
The Cloudron Store is a mechanism to share your app with others who use Cloudron. Currently, to ensure that
apps are maintained, secure and well supported there are some restrictions imposed on apps submitted to
the Cloudron Store. See [#292](https://git.cloudron.io/cloudron/box/issues/292) and [#327](https://git.cloudron.io/cloudron/box/issues/327) for an in-depth discussion.
The following criteria must be met before submitting an app for review:
* You must be willing to relocate your app packaging code to the [Cloudron Git Repo](https://git.cloudron.io/cloudron/).
* Contributed apps must have browser tests. You can see the various [app repos](https://git.cloudron.io/cloudron/) to get an idea on how to write these tests. The Cloudron team can help you write the tests.
* For all practical purposes, you are the maintainer of the app and Cloudron team will not commit to the repo
directly. Any changes will be submitted as Merge Requests.
* You agree that the Cloudron team can take over the responsibility of progressing the app further if you become unresponsive (48 hours), lose interest, lack time etc. Please send us an email if your priorities change.
* You must sign the [Cloudron CLA](https://cla.cloudron.io/).
As a token of our appreciation, 3rd party app authors can use the Cloudron for personal or business use for free.
## Upload for Testing
Once your app is ready, you can upload it to the store for `beta testing` by
other Cloudron users. This can be done using:
```
cloudron appstore upload
```
You should now be able to visit `/#/appstore/<appid>?version=<appversion>` on your
Cloudron to check if the icon, description and other details appear correctly.
Other Cloudron users can install your app on their Cloudron's using
`cloudron install --appstore-id <appid@version>`.
## Publishing
Once you are satisfied with the beta testing, you can submit it for review.
```
cloudron appstore submit
```
The cloudron.io team will review the app and publish the app to the store.
## Versioning and Updates
To create an update for an app, simply bump up the [semver version](/references/manifest.html#version) field in
the manifest and publish a new version to the store.
The Cloudron chooses the next app version to update to based on the following algorithm:
* Choose the maximum `patch` version matching the app's current `major` and `minor` version.
* Failing the above, choose the maximum patch version of the next minor version matching the app's current `major` version.
* Failing the above, choose the maximum patch and minor version of the next major version
For example, let's assume the versions 1.1.3, 1.1.4, 1.1.5, 1.2.4, 1.2.6, 1.3.0, 2.0.0 are published.
* If the app is running 1.1.3, then app will directly update to 1.1.5 (skipping 1.1.4)
* Once in 1.1.5, the app will update to 1.2.6 (skipping 1.2.4)
* Once in 1.2.6, the app will update to 1.3.0
* Once in 1.3.0, the app will update to 2.0.0
The Cloudron admins get notified by email for any major or minor app releases.
## Failed updates
The Cloudron always makes a backup of the app before making an update. Should the
update fail, the user can restore to the backup (which will also restore the app's
code to the previous version).
# Cloudron Button
The [Cloudron Button](/references/button.html) allows anyone to install your application with the click of a button
on their Cloudron.
The button can be added to just about any website including the application's website
and README.md files in GitHub repositories.
# Next steps
Congratulations! You are now well equipped to build web applications for the Cloudron.
You can see some examples of how real apps are packaged here:
* [Lets Chat](https://git.cloudron.io/cloudron/letschat-app)
* [Haste bin](https://git.cloudron.io/cloudron/haste-app)
* [Pasteboard](https://git.cloudron.io/cloudron/pasteboard-app)
+107 -61
View File
@@ -6,34 +6,31 @@ var argv = require('yargs').argv,
autoprefixer = require('gulp-autoprefixer'),
concat = require('gulp-concat'),
cssnano = require('gulp-cssnano'),
del = require('del'),
ejs = require('gulp-ejs'),
gulp = require('gulp'),
rimraf = require('rimraf'),
sass = require('gulp-sass'),
serve = require('gulp-serve'),
sourcemaps = require('gulp-sourcemaps'),
uglify = require('gulp-uglify'),
url = require('url');
uglify = require('gulp-uglify');
gulp.task('3rdparty', function () {
gulp.src([
'webadmin/src/3rdparty/**/*.js',
'webadmin/src/3rdparty/**/*.map',
'webadmin/src/3rdparty/**/*.css',
'webadmin/src/3rdparty/**/*.otf',
'webadmin/src/3rdparty/**/*.eot',
'webadmin/src/3rdparty/**/*.svg',
'webadmin/src/3rdparty/**/*.gif',
'webadmin/src/3rdparty/**/*.ttf',
'webadmin/src/3rdparty/**/*.woff',
'webadmin/src/3rdparty/**/*.woff2'
'src/3rdparty/**/*.js',
'src/3rdparty/**/*.map',
'src/3rdparty/**/*.css',
'src/3rdparty/**/*.otf',
'src/3rdparty/**/*.eot',
'src/3rdparty/**/*.svg',
'src/3rdparty/**/*.gif',
'src/3rdparty/**/*.ttf',
'src/3rdparty/**/*.woff',
'src/3rdparty/**/*.woff2'
])
.pipe(gulp.dest('webadmin/dist/3rdparty/'))
.pipe(gulp.dest('setup/splash/website/3rdparty'));
.pipe(gulp.dest('dist/3rdparty/'));
gulp.src('node_modules/bootstrap-sass/assets/javascripts/bootstrap.min.js')
.pipe(gulp.dest('webadmin/dist/3rdparty/js'))
.pipe(gulp.dest('setup/splash/website/3rdparty/js'));
.pipe(gulp.dest('dist/3rdparty/js'));
});
@@ -46,25 +43,28 @@ if (argv.help || argv.h) {
console.log(' --client-id <clientId>');
console.log(' --client-secret <clientSecret>');
console.log(' --api-origin <cloudron api uri>');
console.log(' --revision <revision>');
process.exit(1);
}
gulp.task('js', ['js-index', 'js-setup', 'js-setupdns', 'js-update'], function () {});
gulp.task('js', ['js-index', 'js-logs', 'js-terminal', 'js-setup', 'js-setupdns', 'js-restore', 'js-update'], function () {});
var oauth = {
clientId: argv.clientId || 'cid-webadmin',
clientSecret: argv.clientSecret || 'unused',
apiOrigin: argv.apiOrigin || '',
apiOriginHostname: argv.apiOrigin ? url.parse(argv.apiOrigin).hostname : ''
};
var revision = argv.revision || '';
console.log();
console.log('Using OAuth credentials:');
console.log(' ClientId: %s', oauth.clientId);
console.log(' ClientSecret: %s', oauth.clientSecret);
console.log(' Cloudron API: %s', oauth.apiOrigin || 'default');
console.log(' Cloudron Host: %s', oauth.apiOriginHostname);
console.log();
console.log('Building for revision: %s', revision);
console.log();
@@ -76,18 +76,50 @@ gulp.task('js-index', function () {
});
gulp.src([
'webadmin/src/js/index.js',
'webadmin/src/js/client.js',
'webadmin/src/js/appstore.js',
'webadmin/src/js/main.js',
'webadmin/src/views/*.js'
'src/js/index.js',
'src/js/client.js',
'src/js/appstore.js',
'src/js/main.js',
'src/views/*.js'
])
.pipe(ejs({ oauth: oauth }, { ext: '.js' }))
.pipe(ejs({ oauth: oauth, revision: revision }, {}, { ext: '.js' }))
.pipe(sourcemaps.init())
.pipe(concat('index.js', { newLine: ';' }))
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('webadmin/dist/js'));
.pipe(gulp.dest('dist/js'));
});
gulp.task('js-logs', function () {
// needs special treatment for error handling
var uglifyer = uglify();
uglifyer.on('error', function (error) {
console.error(error);
});
gulp.src(['src/js/logs.js', 'src/js/client.js'])
.pipe(ejs({ oauth: oauth }, {}, { ext: '.js' }))
.pipe(sourcemaps.init())
.pipe(concat('logs.js', { newLine: ';' }))
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist/js'));
});
gulp.task('js-terminal', function () {
// needs special treatment for error handling
var uglifyer = uglify();
uglifyer.on('error', function (error) {
console.error(error);
});
gulp.src(['src/js/terminal.js', 'src/js/client.js'])
.pipe(ejs({ oauth: oauth }, {}, { ext: '.js' }))
.pipe(sourcemaps.init())
.pipe(concat('terminal.js', { newLine: ';' }))
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist/js'));
});
gulp.task('js-setup', function () {
@@ -97,13 +129,13 @@ gulp.task('js-setup', function () {
console.error(error);
});
gulp.src(['webadmin/src/js/setup.js', 'webadmin/src/js/client.js'])
.pipe(ejs({ oauth: oauth }, { ext: '.js' }))
gulp.src(['src/js/setup.js', 'src/js/client.js'])
.pipe(ejs({ oauth: oauth }, {}, { ext: '.js' }))
.pipe(sourcemaps.init())
.pipe(concat('setup.js', { newLine: ';' }))
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('webadmin/dist/js'));
.pipe(gulp.dest('dist/js'));
});
gulp.task('js-setupdns', function () {
@@ -113,15 +145,32 @@ gulp.task('js-setupdns', function () {
console.error(error);
});
gulp.src(['webadmin/src/js/setupdns.js', 'webadmin/src/js/client.js'])
.pipe(ejs({ oauth: oauth }, { ext: '.js' }))
gulp.src(['src/js/setupdns.js', 'src/js/client.js'])
.pipe(ejs({ oauth: oauth }, {}, { ext: '.js' }))
.pipe(sourcemaps.init())
.pipe(concat('setupdns.js', { newLine: ';' }))
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('webadmin/dist/js'));
.pipe(gulp.dest('dist/js'));
});
gulp.task('js-restore', function () {
// needs special treatment for error handling
var uglifyer = uglify();
uglifyer.on('error', function (error) {
console.error(error);
});
gulp.src(['src/js/restore.js', 'src/js/client.js'])
.pipe(ejs({ oauth: oauth }, {}, { ext: '.js' }))
.pipe(sourcemaps.init())
.pipe(concat('restore.js', { newLine: ';' }))
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('dist/js'));
});
gulp.task('js-update', function () {
// needs special treatment for error handling
var uglifyer = uglify();
@@ -129,12 +178,11 @@ gulp.task('js-update', function () {
console.error(error);
});
gulp.src(['webadmin/src/js/update.js'])
gulp.src(['src/js/update.js'])
.pipe(sourcemaps.init())
.pipe(uglifyer)
.pipe(sourcemaps.write())
.pipe(gulp.dest('webadmin/dist/js'))
.pipe(gulp.dest('setup/splash/website/js'));
.pipe(gulp.dest('dist/js'));
});
@@ -142,20 +190,16 @@ gulp.task('js-update', function () {
// HTML
// --------------
gulp.task('html', ['html-views', 'html-update', 'html-templates'], function () {
return gulp.src('webadmin/src/*.html').pipe(ejs({ apiOriginHostname: oauth.apiOriginHostname }, { ext: '.html' })).pipe(gulp.dest('webadmin/dist'));
});
gulp.task('html-update', function () {
return gulp.src(['webadmin/src/update.html']).pipe(gulp.dest('setup/splash/website'));
gulp.task('html', ['html-views', 'html-templates'], function () {
return gulp.src('src/*.html').pipe(ejs({ revision: revision }, {}, { ext: '.html' })).pipe(gulp.dest('dist'));
});
gulp.task('html-views', function () {
return gulp.src('webadmin/src/views/**/*.html').pipe(gulp.dest('webadmin/dist/views'));
return gulp.src('src/views/**/*.html').pipe(gulp.dest('dist/views'));
});
gulp.task('html-templates', function () {
return gulp.src('webadmin/src/templates/**/*.html').pipe(gulp.dest('webadmin/dist/templates'));
return gulp.src('src/templates/**/*.html').pipe(gulp.dest('dist/templates'));
});
// --------------
@@ -163,19 +207,18 @@ gulp.task('html-templates', function () {
// --------------
gulp.task('css', function () {
return gulp.src('webadmin/src/*.scss')
return gulp.src('src/*.scss')
.pipe(sourcemaps.init())
.pipe(sass({ includePaths: ['node_modules/bootstrap-sass/assets/stylesheets/'] }).on('error', sass.logError))
.pipe(autoprefixer())
.pipe(cssnano())
.pipe(sourcemaps.write())
.pipe(gulp.dest('webadmin/dist'))
.pipe(gulp.dest('setup/splash/website'));
.pipe(gulp.dest('dist'));
});
gulp.task('images', function () {
return gulp.src('webadmin/src/img/**')
.pipe(gulp.dest('webadmin/dist/img'));
return gulp.src('src/img/**')
.pipe(gulp.dest('dist/img'));
});
// --------------
@@ -183,22 +226,25 @@ gulp.task('images', function () {
// --------------
gulp.task('watch', ['default'], function () {
gulp.watch(['webadmin/src/*.scss'], ['css']);
gulp.watch(['webadmin/src/img/*'], ['images']);
gulp.watch(['webadmin/src/**/*.html'], ['html']);
gulp.watch(['webadmin/src/views/*.html'], ['html-views']);
gulp.watch(['webadmin/src/templates/*.html'], ['html-templates']);
gulp.watch(['webadmin/src/js/update.js'], ['js-update']);
gulp.watch(['webadmin/src/js/setup.js', 'webadmin/src/js/client.js'], ['js-setup']);
gulp.watch(['webadmin/src/js/setupdns.js', 'webadmin/src/js/client.js'], ['js-setupdns']);
gulp.watch(['webadmin/src/js/index.js', 'webadmin/src/js/client.js', 'webadmin/src/js/appstore.js', 'webadmin/src/js/main.js', 'webadmin/src/views/*.js'], ['js-index']);
gulp.watch(['webadmin/src/3rdparty/**/*'], ['3rdparty']);
gulp.watch(['src/*.scss'], ['css']);
gulp.watch(['src/img/*'], ['images']);
gulp.watch(['src/**/*.html'], ['html']);
gulp.watch(['src/views/*.html'], ['html-views']);
gulp.watch(['src/templates/*.html'], ['html-templates']);
gulp.watch(['src/js/update.js'], ['js-update']);
gulp.watch(['src/js/setup.js', 'src/js/client.js'], ['js-setup']);
gulp.watch(['src/js/setupdns.js', 'src/js/client.js'], ['js-setupdns']);
gulp.watch(['src/js/restore.js', 'src/js/client.js'], ['js-restore']);
gulp.watch(['src/js/logs.js', 'src/js/client.js'], ['js-logs']);
gulp.watch(['src/js/terminal.js', 'src/js/client.js'], ['js-terminal']);
gulp.watch(['src/js/index.js', 'src/js/client.js', 'src/js/appstore.js', 'src/js/main.js', 'src/views/*.js'], ['js-index']);
gulp.watch(['src/3rdparty/**/*'], ['3rdparty']);
});
gulp.task('clean', function () {
del.sync(['webadmin/dist', 'setup/splash/website']);
rimraf.sync('dist');
});
gulp.task('default', ['clean', 'html', 'js', '3rdparty', 'images', 'css'], function () {});
gulp.task('develop', ['watch'], serve({ root: 'webadmin/dist', port: 4000 }));
gulp.task('develop', ['watch'], serve({ root: 'dist', port: 4000 }));
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env node
'use strict';
var tar = require('tar-fs'),
fs = require('fs'),
path = require('path'),
zlib = require('zlib');
if (process.argv.length < 4) {
console.error('Usage: tarjs <cwd> <dir>');
process.exit(1);
}
var dir = process.argv[3];
var cwd = process.argv[2];
console.error('Packing directory "'+ dir +'" from within "' + cwd + '" and stream to stdout');
process.chdir(cwd);
var stat = fs.statSync(dir);
if (!stat.isDirectory()) throw(dir + ' is not a directory');
var gzipStream = zlib.createGzip({});
tar.pack(path.resolve(dir), {
ignore: function (name) {
if (name === '.') return true;
return false;
}
}).pipe(gzipStream).pipe(process.stdout);
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

-14
View File
@@ -1,14 +0,0 @@
'use strict';
var url = require('url');
exports.up = function(db, callback) {
var dbName = url.parse(process.env.DATABASE_URL).path.substr(1); // remove slash
// by default, mysql collates case insensitively. 'utf8_general_cs' is not available
db.runSql('ALTER DATABASE ' + dbName + ' DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE utf8_bin', callback);
};
exports.down = function(db, callback) {
callback();
};
-18
View File
@@ -1,18 +0,0 @@
'use strict';
var fs = require('fs'),
async = require('async'),
path = require('path');
exports.up = function(db, callback) {
var schema = fs.readFileSync(path.join(__dirname, 'initial-schema.sql')).toString('utf8');
var statements = schema.split(';');
async.eachSeries(statements, function (statement, callback) {
if (statement.trim().length === 0) return callback(null);
db.runSql(statement, callback);
}, callback);
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE users, tokens, clients, apps, appPortBindings, authcodes, settings', callback);
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN resetToken VARCHAR(128) DEFAULT ""', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN resetToken', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,19 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('DELETE FROM tokens', [], function (error) {
if (error) console.error(error);
db.runSql('ALTER TABLE tokens MODIFY expires BIGINT', [], function (error) {
if (error) console.error(error);
callback(error);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE tokens MODIFY expires VARCHAR(512)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE authcodes ADD COLUMN expiresAt BIGINT NOT NULL', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE authcodes DROP COLUMN expiresAt', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE appPortBindings ADD COLUMN environmentVariable VARCHAR(128) NOT NULL', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE appPortBindings DROP COLUMN environmentVariable', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE appPortBindings DROP COLUMN containerPort', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE appPortBindings ADD COLUMN containerPort VARCHAR(5) NOT NULL', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,19 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('DELETE FROM tokens', [], function (error) {
if (error) console.error(error);
db.runSql('ALTER TABLE tokens CHANGE userId identifier VARCHAR(128) NOT NULL', [], function (error) {
if (error) console.error(error);
callback(error);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE tokens CHANGE identifier userId VARCHAR(128) NOT NULL', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN version', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN version VARCHAR(32)', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN healthy, ADD COLUMN health VARCHAR(128)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN health, ADD COLUMN healthy INTEGER', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN lastBackupId VARCHAR(128)', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN lastBackupId', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN createdAt', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,10 +0,0 @@
'use strict';
exports.up = function(db, callback) {
// everyday at 1am
db.runSql('INSERT settings (name, value) VALUES("autoupdate_pattern", ?)', [ '00 00 1 * * *' ], callback);
};
exports.down = function(db, callback) {
db.runSql('DELETE * FROM settings WHERE name="autoupdate_pattern"', [ ], callback);
};
@@ -1,14 +0,0 @@
'use strict';
var safe = require('safetydance');
exports.up = function(db, callback) {
var tz = safe.fs.readFileSync('/etc/timezone', 'utf8');
tz = tz ? tz.trim() : 'America/Los_Angeles';
db.runSql('INSERT settings (name, value) VALUES("time_zone", ?)', [ tz ], callback);
};
exports.down = function(db, callback) {
db.runSql('DELETE * FROM settings WHERE name="time_zone"', [ ], callback);
};
@@ -1,24 +0,0 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
// http://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address
async.series([
db.runSql.bind(db, 'ALTER TABLE users MODIFY username VARCHAR(254)'),
db.runSql.bind(db, 'ALTER TABLE users ADD CONSTRAINT users_username UNIQUE (username)'),
db.runSql.bind(db, 'ALTER TABLE users MODIFY email VARCHAR(254)'),
db.runSql.bind(db, 'ALTER TABLE users ADD CONSTRAINT users_email UNIQUE (email)'),
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE users DROP INDEX users_username'),
db.runSql.bind(db, 'ALTER TABLE users MODIFY username VARCHAR(512)'),
db.runSql.bind(db, 'ALTER TABLE users DROP INDEX users_email'),
db.runSql.bind(db, 'ALTER TABLE users MODIFY email VARCHAR(512)'),
], callback);
};
@@ -1,17 +0,0 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE users MODIFY username VARCHAR(254) NOT NULL'),
db.runSql.bind(db, 'ALTER TABLE users MODIFY email VARCHAR(254) NOT NULL'),
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE users MODIFY username VARCHAR(254)'),
db.runSql.bind(db, 'ALTER TABLE users MODIFY email VARCHAR(254)'),
], callback);
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN lastManifestJson VARCHAR(2048)', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN lastManifestJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps CHANGE lastManifestJson lastBackupConfigJson VARCHAR(2048)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps CHANGE lastBackupConfigJson lastManifestJson VARCHAR(2048)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN oldConfigJson VARCHAR(2048)', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN oldConfigJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,9 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('DELETE FROM settings', [ ], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN oauthProxy BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN oauthProxy', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,17 +0,0 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'DELETE FROM clients'),
db.runSql.bind(db, 'ALTER TABLE clients ADD COLUMN type VARCHAR(16) NOT NULL'),
], callback);
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE clients DROP COLUMN type', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps CHANGE accessRestriction accessRestrictionJson VARCHAR(2048)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps CHANGE accessRestrictionJson accessRestriction VARCHAR(2048)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps MODIFY manifestJson TEXT', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps MODIFY manifestJson VARCHAR(2048)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,19 +0,0 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps MODIFY accessRestrictionJson TEXT'),
db.runSql.bind(db, 'ALTER TABLE apps MODIFY lastBackupConfigJson TEXT'),
db.runSql.bind(db, 'ALTER TABLE apps MODIFY oldConfigJson TEXT')
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps MODIFY accessRestrictionJson VARCHAR(2048)'),
db.runSql.bind(db, 'ALTER TABLE apps MODIFY lastBackupConfigJson VARCHAR(2048)'),
db.runSql.bind(db, 'ALTER TABLE apps MODIFY oldConfigJson VARCHAR(2048)')
], callback);
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN displayName VARCHAR(512) DEFAULT ""', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN displayName', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN memoryLimit BIGINT DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN memoryLimit', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,20 +0,0 @@
'use strict';
exports.up = function(db, callback) {
var cmd = "CREATE TABLE groups(" +
"id VARCHAR(128) NOT NULL UNIQUE," +
"name VARCHAR(128) NOT NULL UNIQUE," +
"PRIMARY KEY(id))";
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE groups', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,21 +0,0 @@
'use strict';
exports.up = function(db, callback) {
var cmd = "CREATE TABLE IF NOT EXISTS groupMembers(" +
"groupId VARCHAR(128) NOT NULL," +
"userId VARCHAR(128) NOT NULL," +
"FOREIGN KEY(groupId) REFERENCES groups(id)," +
"FOREIGN KEY(userId) REFERENCES users(id));";
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE groupMembers', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,29 +0,0 @@
'use strict';
var async = require('async');
var ADMIN_GROUP_ID = 'admin'; // see groups.js
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'START TRANSACTION;'),
db.runSql.bind(db, 'INSERT INTO groups (id, name) VALUES (?, ?)', [ ADMIN_GROUP_ID, 'admin' ]),
function migrateAdminFlag(done) {
db.all('SELECT * FROM users WHERE admin=1', function (error, results) {
if (error) return done(error);
console.dir(results);
async.eachSeries(results, function (r, next) {
db.runSql('INSERT INTO groupMembers (groupId, userId) VALUES (?, ?)', [ ADMIN_GROUP_ID, r.id ], next);
}, done);
});
},
db.runSql.bind(db, 'ALTER TABLE users DROP COLUMN admin'),
db.runSql.bind(db, 'COMMIT')
], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -1,24 +0,0 @@
'use strict';
exports.up = function(db, callback) {
var cmd = "CREATE TABLE backups(" +
"filename VARCHAR(128) NOT NULL," +
"creationTime TIMESTAMP," +
"version VARCHAR(128) NOT NULL," +
"type VARCHAR(16) NOT NULL," +
"dependsOn VARCHAR(4096)," +
"state VARCHAR(16) NOT NULL," +
"PRIMARY KEY (filename))";
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE backups', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups ADD COLUMN configJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups DROP COLUMN configJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups DROP COLUMN configJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups ADD COLUMN configJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups CHANGE filename id VARCHAR(128)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups CHANGE id filename VARCHAR(128)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users MODIFY username VARCHAR(254) UNIQUE', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users MODIFY username VARCHAR(254) NOT NULL UNIQUE', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN altDomain VARCHAR(256)', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN altDomain', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,23 +0,0 @@
'use strict';
exports.up = function(db, callback) {
var cmd = "CREATE TABLE eventlog(" +
"id VARCHAR(128) NOT NULL," +
"source JSON," +
"creationTime TIMESTAMP," +
"action VARCHAR(128) NOT NULL," +
"data JSON," +
"PRIMARY KEY (id))";
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE eventlog', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN showTutorial BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN showTutorial', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,21 +0,0 @@
'use strict';
exports.up = function(db, callback) {
var cmd = 'CREATE TABLE mailboxes(' +
'name VARCHAR(128) NOT NULL,' +
'aliasTarget VARCHAR(128),' +
'creationTime TIMESTAMP,' +
'PRIMARY KEY (name))';
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE mailboxes', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,26 +0,0 @@
'use strict';
var async = require('async');
// imports mailbox entries for existing users
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'START TRANSACTION;'),
function addUserMailboxes(done) {
db.all('SELECT username FROM users', function (error, results) {
if (error) return done(error);
async.eachSeries(results, function (r, next) {
if (!r.username) return next();
db.runSql('INSERT INTO mailboxes (name) VALUES (?)', [ r.username ], next);
}, done);
});
},
db.runSql.bind(db, 'COMMIT')
], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN lastBackupConfigJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN lastBackupConfigJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps MODIFY installationProgress TEXT', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps MODIFY installationProgress VARCHAR(512)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN xFrameOptions VARCHAR(512)', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN xFrameOptions', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.all('SELECT id FROM users', function (error, results) {
if (error) return callback(error);
// existing cloudrons have email enabled by default. future cloudrons will have it disabled by default
var enable = results.length !== 0;
db.runSql('INSERT settings (name, value) VALUES("mail_config", ?)', [ JSON.stringify({ enabled: enable }) ], callback);
});
};
exports.down = function(db, callback) {
db.runSql('DELETE * FROM settings WHERE name="mail_config"', [ ], callback);
};
@@ -1,73 +0,0 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE mailboxes ADD COLUMN ownerId VARCHAR(128)'),
db.runSql.bind(db, 'ALTER TABLE mailboxes ADD COLUMN ownerType VARCHAR(16)'),
db.runSql.bind(db, 'START TRANSACTION;'),
function addGroupMailboxes(done) {
console.log('Importing group mailboxes');
db.all('SELECT id, name FROM groups', function (error, results) {
if (error) return done(error);
async.eachSeries(results, function (g, next) {
db.runSql('INSERT INTO mailboxes (ownerId, ownerType, name) VALUES (?, ?, ?)', [ g.id, 'group', g.name ], function (error) {
if (error) console.error('Error importing group ' + JSON.stringify(g) + error);
next();
});
}, done);
});
},
function addAppMailboxes(done) {
console.log('Importing app mail boxes');
db.all('SELECT id, location, manifestJson FROM apps', function (error, results) {
if (error) return done(error);
async.eachSeries(results, function (a, next) {
var manifest = JSON.parse(a.manifestJson);
if (!manifest.addons['sendmail'] && !manifest.addons['recvmail']) return next();
var mailboxName = (a.location ? a.location : manifest.title.replace(/[^a-zA-Z0-9]/g, '')) + '.app';
db.runSql('INSERT INTO mailboxes (ownerId, ownerType, name) VALUES (?, ?, ?)', [ a.id, 'app', mailboxName ], function (error) {
if (error) console.error('Error importing app ' + JSON.stringify(a) + error);
next();
});
}, done);
});
},
function setUserMailboxOwnerIds(done) {
console.log('Setting owner id of user mailboxes and aliases');
db.all('SELECT id, username FROM users', function (error, results) {
if (error) return done(error);
async.eachSeries(results, function (u, next) {
if (!u.username) return next();
db.runSql('UPDATE mailboxes SET ownerId = ?, ownerType = ? WHERE name = ? OR aliasTarget = ?', [ u.id, 'user', u.username, u.username ], function (error) {
if (error) console.error('Error setting ownerid ' + JSON.stringify(u) + error);
next();
});
}, done);
});
},
db.runSql.bind(db, 'COMMIT'),
db.runSql.bind(db, 'ALTER TABLE mailboxes MODIFY ownerId VARCHAR(128) NOT NULL'),
db.runSql.bind(db, 'ALTER TABLE mailboxes MODIFY ownerType VARCHAR(128) NOT NULL'),
], callback);
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE mailboxes DROP COLUMN ownerId', function (error) {
if (error) console.error(error);
db.runSql('ALTER TABLE mailboxes DROP COLUMN ownerType', function (error) {
if (error) console.error(error);
callback(error);
});
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN sso BOOLEAN DEFAULT 1', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN sso', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN oauthProxy', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN oauthProxy BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN showTutorial', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN showTutorial BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN debugModeJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN debugModeJson ', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,15 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups MODIFY dependsOn TEXT', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups MODIFY dependsOn VARCHAR(4096)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -1,16 +0,0 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE appAddonConfigs ADD COLUMN name VARCHAR(128)', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE appAddonConfigs DROP COLUMN name', function (error) {
if (error) console.error(error);
callback(error);
});
};

Some files were not shown because too many files have changed in this diff Show More