diff --git a/.travis.yml b/.travis.yml
index 814e766c0..fdf9bd708 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,4 +21,4 @@ after_success:
- bash <(curl -s https://codecov.io/bash)
go:
-- 1.7.3
+- 1.7.4
diff --git a/Makefile b/Makefile
index 772032d07..57bdf888e 100644
--- a/Makefile
+++ b/Makefile
@@ -71,64 +71,64 @@ verifiers: vet fmt lint cyclo spelling
vet:
@echo "Running $@:"
- @GO15VENDOREXPERIMENT=1 go tool vet -all ./cmd
- @GO15VENDOREXPERIMENT=1 go tool vet -all ./pkg
- @GO15VENDOREXPERIMENT=1 go tool vet -shadow=true ./cmd
- @GO15VENDOREXPERIMENT=1 go tool vet -shadow=true ./pkg
+ @go tool vet -all ./cmd
+ @go tool vet -all ./pkg
+ @go tool vet -shadow=true ./cmd
+ @go tool vet -shadow=true ./pkg
fmt:
@echo "Running $@:"
- @GO15VENDOREXPERIMENT=1 gofmt -s -l cmd
- @GO15VENDOREXPERIMENT=1 gofmt -s -l pkg
+ @gofmt -s -l cmd
+ @gofmt -s -l pkg
lint:
@echo "Running $@:"
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/golint -set_exit_status github.com/minio/minio/cmd...
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/golint -set_exit_status github.com/minio/minio/pkg...
+ @${GOPATH}/bin/golint -set_exit_status github.com/minio/minio/cmd...
+ @${GOPATH}/bin/golint -set_exit_status github.com/minio/minio/pkg...
ineffassign:
@echo "Running $@:"
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/ineffassign .
+ @${GOPATH}/bin/ineffassign .
cyclo:
@echo "Running $@:"
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/gocyclo -over 100 cmd
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/gocyclo -over 100 pkg
+ @${GOPATH}/bin/gocyclo -over 100 cmd
+ @${GOPATH}/bin/gocyclo -over 100 pkg
build: getdeps verifiers $(UI_ASSETS)
deadcode:
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/deadcode
+ @${GOPATH}/bin/deadcode
spelling:
- @-GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/misspell -error `find cmd/`
- @-GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/misspell -error `find pkg/`
- @-GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/misspell -error `find docs/`
+ @${GOPATH}/bin/misspell -error `find cmd/`
+ @${GOPATH}/bin/misspell -error `find pkg/`
+ @${GOPATH}/bin/misspell -error `find docs/`
test: build
@echo "Running all minio testing:"
- @GO15VENDOREXPERIMENT=1 go test $(GOFLAGS) github.com/minio/minio/cmd...
- @GO15VENDOREXPERIMENT=1 go test $(GOFLAGS) github.com/minio/minio/pkg...
+ @go test $(GOFLAGS) github.com/minio/minio/cmd...
+ @go test $(GOFLAGS) github.com/minio/minio/pkg...
coverage: build
@echo "Running all coverage for minio:"
- @GO15VENDOREXPERIMENT=1 ./buildscripts/go-coverage.sh
+ @./buildscripts/go-coverage.sh
gomake-all: build
@echo "Installing minio:"
- @GO15VENDOREXPERIMENT=1 go build --ldflags $(BUILD_LDFLAGS) -o $(GOPATH)/bin/minio
+ @go build --ldflags $(BUILD_LDFLAGS) -o $(GOPATH)/bin/minio
pkg-add:
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/govendor add $(PKG)
+ ${GOPATH}/bin/govendor add $(PKG)
pkg-update:
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/govendor update $(PKG)
+ ${GOPATH}/bin/govendor update $(PKG)
pkg-remove:
- @GO15VENDOREXPERIMENT=1 ${GOPATH}/bin/govendor remove $(PKG)
+ ${GOPATH}/bin/govendor remove $(PKG)
pkg-list:
- @GO15VENDOREXPERIMENT=1 $(GOPATH)/bin/govendor list
+ @$(GOPATH)/bin/govendor list
install: gomake-all
diff --git a/README.md b/README.md
index 885df8b50..82b508ecb 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Minio Quickstart Guide [](https://gitter.im/minio/minio?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://goreportcard.com/report/minio/minio) [](https://hub.docker.com/r/minio/minio/) [](https://codecov.io/gh/minio/minio)
+# Minio Quickstart Guide [](https://slack.minio.io) [](https://goreportcard.com/report/minio/minio) [](https://hub.docker.com/r/minio/minio/) [](https://codecov.io/gh/minio/minio)
Minio is an object storage server released under Apache License v2.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB.
@@ -6,15 +6,15 @@ Minio server is light enough to be bundled with the application stack, similar t
## Docker Container
### Stable
-```sh
-$ docker pull minio/minio
-$ docker run -p 9000:9000 minio/minio server /export
+```
+docker pull minio/minio
+docker run -p 9000:9000 minio/minio server /export
```
### Edge
-```sh
-$ docker pull minio/minio:edge
-$ docker run -p 9000:9000 minio/minio:edge server /export
+```
+docker pull minio/minio:edge
+docker run -p 9000:9000 minio/minio:edge server /export
```
Please visit Minio Docker quickstart guide for more [here](https://docs.minio.io/docs/minio-docker-quickstart-guide)
@@ -23,8 +23,8 @@ Please visit Minio Docker quickstart guide for more [here](https://docs.minio.io
Install minio packages using [Homebrew](http://brew.sh/)
```sh
-$ brew install minio
-$ minio server ~/Photos
+brew install minio
+minio server ~/Photos
```
### Binary Download
@@ -32,8 +32,8 @@ $ minio server ~/Photos
| ----------| -------- | ------|
|Apple OS X|64-bit Intel|https://dl.minio.io/server/minio/release/darwin-amd64/minio|
```sh
-$ chmod 755 minio
-$ ./minio server ~/Photos
+chmod 755 minio
+./minio server ~/Photos
```
## GNU/Linux
@@ -44,8 +44,8 @@ $ ./minio server ~/Photos
||32-bit Intel|https://dl.minio.io/server/minio/release/linux-386/minio|
||32-bit ARM|https://dl.minio.io/server/minio/release/linux-arm/minio|
```sh
-$ chmod +x minio
-$ ./minio server ~/Photos
+chmod +x minio
+./minio server ~/Photos
```
## Microsoft Windows
@@ -55,7 +55,7 @@ $ ./minio server ~/Photos
|Microsoft Windows|64-bit|https://dl.minio.io/server/minio/release/windows-amd64/minio.exe|
||32-bit|https://dl.minio.io/server/minio/release/windows-386/minio.exe|
```sh
-C:\Users\Username\Downloads> minio.exe server D:\Photos
+minio.exe server D:\Photos
```
## FreeBSD
@@ -64,10 +64,11 @@ C:\Users\Username\Downloads> minio.exe server D:\Photos
| ----------| -------- | ------|
|FreeBSD|64-bit|https://dl.minio.io/server/minio/release/freebsd-amd64/minio|
```sh
-$ chmod 755 minio
-$ ./minio server ~/Photos
+chmod 755 minio
+./minio server ~/Photos
```
-Please visit official zfs FreeBSD guide for more details [here](https://www.freebsd.org/doc/handbook/zfs-quickstart.html)
+
+You can run Minio on FreeBSD with FreeNAS storage-backend - see [here](https://docs.minio.io/docs/how-to-run-minio-in-freenas) for more details.
## Install from Source
@@ -75,7 +76,7 @@ Source installation is only intended for developers and advanced users. If you d
```sh
-$ go get -u github.com/minio/minio
+go get -u github.com/minio/minio
```
diff --git a/README_ZH.md b/README_ZH.md
index 64aacb1b9..8e911a4ae 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -1,4 +1,4 @@
-# Minio 快速入门 [](https://gitter.im/minio/minio?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://goreportcard.com/report/minio/minio) [](https://codecov.io/gh/minio/minio)
+# Minio 快速入门 [](https://slack.minio.io) [](https://goreportcard.com/report/minio/minio) [](https://codecov.io/gh/minio/minio)
Minio是一个对象存储服务,基于Apache License v2.0协议. 它完全兼容亚马逊的S3云储存服务,非常适合于存储很多非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
diff --git a/appveyor.yml b/appveyor.yml
index 088fe69db..7488c103a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -12,7 +12,6 @@ clone_folder: c:\gopath\src\github.com\minio\minio
# Environment variables
environment:
GOPATH: c:\gopath
- GO15VENDOREXPERIMENT: 1
# scripts that run after cloning repository
install:
@@ -36,8 +35,8 @@ test_script:
# Unit tests
- ps: Add-AppveyorTest "Unit Tests" -Outcome Running
- mkdir build\coverage
- - go test -race github.com/minio/minio/cmd...
- - go test -race github.com/minio/minio/pkg...
+ - go test -timeout 15m -v -race github.com/minio/minio/cmd...
+ - go test -v -race github.com/minio/minio/pkg...
- go test -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd
- ps: Update-AppveyorTest "Unit Tests" -Outcome Passed
diff --git a/browser/.babelrc b/browser/.babelrc
new file mode 100644
index 000000000..9cd3d553f
--- /dev/null
+++ b/browser/.babelrc
@@ -0,0 +1,8 @@
+{
+ "presets": [
+ "es2015",
+ "react"
+ ],
+
+ "plugins": ["transform-object-rest-spread"]
+}
diff --git a/browser/.editorconfig b/browser/.editorconfig
new file mode 100644
index 000000000..92926b6de
--- /dev/null
+++ b/browser/.editorconfig
@@ -0,0 +1,16 @@
+# editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.json]
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/browser/.esformatter b/browser/.esformatter
new file mode 100644
index 000000000..1677d7c4b
--- /dev/null
+++ b/browser/.esformatter
@@ -0,0 +1,23 @@
+{
+ "plugins": [
+ "esformatter-jsx"
+ ],
+ // Copied from https://github.com/royriojas/esformatter-jsx
+ "jsx": {
+ "formatJSX": true, //Duh! that's the default
+ "attrsOnSameLineAsTag": false, // move each attribute to its own line
+ "maxAttrsOnTag": 3, // if lower or equal than 3 attributes, they will be kept on a single line
+ "firstAttributeOnSameLine": true, // keep the first attribute in the same line as the tag
+ "formatJSXExpressions": true, // default true, if false jsxExpressions won't be recursively formatted
+ "JSXExpressionsSingleLine": true, // default true, if false the JSXExpressions might span several lines
+ "alignWithFirstAttribute": false, // do not align attributes with the first tag
+ "spaceInJSXExpressionContainers": " ", // default to one space. Make it empty if you don't like spaces between JSXExpressionContainers
+ "removeSpaceBeforeClosingJSX": false, // default false. if true =>
+ "closingTagOnNewLine": false, // default false. if true attributes on multiple lines will close the tag on a new line
+ "JSXAttributeQuotes": "", // possible values "single" or "double". Leave it as empty string if you don't want to modify the attributes' quotes
+ "htmlOptions": {
+ // put here the options for js-beautify.html
+ }
+ }
+}
+
diff --git a/browser/.gitignore b/browser/.gitignore
new file mode 100644
index 000000000..6e4ed66d7
--- /dev/null
+++ b/browser/.gitignore
@@ -0,0 +1,20 @@
+**/*.swp
+cover.out
+*~
+minio
+!*/
+site/
+**/*.test
+**/*.sublime-workspace
+/.idea/
+/Minio.iml
+**/access.log
+build
+vendor/**/*.js
+vendor/**/*.json
+release
+.DS_Store
+*.syso
+coverage.txt
+node_modules
+production
diff --git a/browser/README.md b/browser/README.md
new file mode 100644
index 000000000..74bb563f2
--- /dev/null
+++ b/browser/README.md
@@ -0,0 +1,37 @@
+# Minio File Browser
+
+``Minio Browser`` provides minimal set of UI to manage buckets and objects on ``minio`` server. ``Minio Browser`` is written in javascript and released under [Apache 2.0 License](./LICENSE).
+
+## Installation
+
+### Install yarn:
+```sh
+$ curl -o- -L https://yarnpkg.com/install.sh | bash
+$ yarn
+```
+
+### Install `go-bindata` and `go-bindata-assetfs`.
+
+If you do not have a working Golang environment, please follow [Install Golang](https://docs.minio.io/docs/how-to-install-golang)
+
+```sh
+$ go get github.com/jteeuwen/go-bindata/...
+$ go get github.com/elazarl/go-bindata-assetfs/...
+```
+
+## Generating Assets.
+
+### Generate ui-assets.go
+
+```sh
+$ yarn release
+```
+This generates ui-assets.go in the current direcotry. Now do `make` in the parent directory to build the minio binary with the newly generated ui-assets.go
+
+### Run Minio Browser with live reload.
+
+```sh
+$ yarn dev
+```
+
+Open [http://localhost:8080/minio/](http://localhost:8080/minio/) in your browser to play with the application
diff --git a/browser/app/css/loader.css b/browser/app/css/loader.css
new file mode 100644
index 000000000..d7ae07b02
--- /dev/null
+++ b/browser/app/css/loader.css
@@ -0,0 +1,98 @@
+.page-load {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background: #32393F;
+ z-index: 100;
+ transition: opacity 200ms;
+ -webkit-transition: opacity 200ms;
+}
+
+.pl-0{
+ opacity: 0;
+}
+
+.pl-1 {
+ display: none;
+}
+
+.pl-inner {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ left: 50%;
+ margin-left: -50px;
+ top: 50%;
+ margin-top: -50px;
+ text-align: center;
+ -webkit-animation: fade-in 500ms;
+ animation: fade-in 500ms;
+ -webkit-animation-fill-mode: both;
+ animation-fill-mode: both;
+ animation-delay: 350ms;
+ -webkit-animation-delay: 350ms;
+ -webkit-backface-visibility: visible;
+ backface-visibility: visible;
+}
+
+.pl-inner:before {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ display: block;
+ -webkit-animation: spin 1000ms infinite linear;
+ animation: spin 1000ms infinite linear;
+ border: 1px solid rgba(255, 255, 255, 0.2);;
+ border-left-color: #fff;
+ border-radius: 50%;
+}
+
+.pl-inner > img {
+ width: 30px;
+ margin-top: 28px;
+}
+
+@-webkit-keyframes fade-in {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes fade-in {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@-webkit-keyframes spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
diff --git a/browser/app/fonts/lato/lato-normal.woff b/browser/app/fonts/lato/lato-normal.woff
new file mode 100755
index 000000000..f2317755c
Binary files /dev/null and b/browser/app/fonts/lato/lato-normal.woff differ
diff --git a/browser/app/fonts/lato/lato-normal.woff2 b/browser/app/fonts/lato/lato-normal.woff2
new file mode 100755
index 000000000..2a119ebd5
Binary files /dev/null and b/browser/app/fonts/lato/lato-normal.woff2 differ
diff --git a/browser/app/img/arrow.svg b/browser/app/img/arrow.svg
new file mode 100644
index 000000000..fb5574ff8
--- /dev/null
+++ b/browser/app/img/arrow.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/browser/app/img/browsers/chrome.png b/browser/app/img/browsers/chrome.png
new file mode 100644
index 000000000..278ef4d15
Binary files /dev/null and b/browser/app/img/browsers/chrome.png differ
diff --git a/browser/app/img/browsers/firefox.png b/browser/app/img/browsers/firefox.png
new file mode 100644
index 000000000..2803f10a7
Binary files /dev/null and b/browser/app/img/browsers/firefox.png differ
diff --git a/browser/app/img/browsers/safari.png b/browser/app/img/browsers/safari.png
new file mode 100644
index 000000000..4ed52b904
Binary files /dev/null and b/browser/app/img/browsers/safari.png differ
diff --git a/browser/app/img/favicon.ico b/browser/app/img/favicon.ico
new file mode 100644
index 000000000..0718efa83
Binary files /dev/null and b/browser/app/img/favicon.ico differ
diff --git a/browser/app/img/logo.svg b/browser/app/img/logo.svg
new file mode 100644
index 000000000..e4fa2b767
--- /dev/null
+++ b/browser/app/img/logo.svg
@@ -0,0 +1,57 @@
+
+
diff --git a/browser/app/img/more-h-light.svg b/browser/app/img/more-h-light.svg
new file mode 100644
index 000000000..0c2e2da60
--- /dev/null
+++ b/browser/app/img/more-h-light.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/browser/app/img/more-h.svg b/browser/app/img/more-h.svg
new file mode 100644
index 000000000..cf69dcf6b
--- /dev/null
+++ b/browser/app/img/more-h.svg
@@ -0,0 +1 @@
+
diff --git a/browser/app/img/select-caret.svg b/browser/app/img/select-caret.svg
new file mode 100644
index 000000000..b2b26b86b
--- /dev/null
+++ b/browser/app/img/select-caret.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/browser/app/index.html b/browser/app/index.html
new file mode 100644
index 000000000..dfc0f555b
--- /dev/null
+++ b/browser/app/index.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ Minio Browser
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/app/index.js b/browser/app/index.js
new file mode 100644
index 000000000..d750577bc
--- /dev/null
+++ b/browser/app/index.js
@@ -0,0 +1,116 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import './less/main.less'
+
+import React from 'react'
+import ReactDOM from 'react-dom'
+import thunkMiddleware from 'redux-thunk'
+import createStore from 'redux/lib/createStore'
+import applyMiddleware from 'redux/lib/applyMiddleware'
+
+import Route from 'react-router/lib/Route'
+import Router from 'react-router/lib/Router'
+import browserHistory from 'react-router/lib/browserHistory'
+import IndexRoute from 'react-router/lib/IndexRoute'
+
+import Provider from 'react-redux/lib/components/Provider'
+import connect from 'react-redux/lib/components/connect'
+
+import Moment from 'moment'
+
+import { minioBrowserPrefix } from './js/constants.js'
+import * as actions from './js/actions.js'
+import reducer from './js/reducers.js'
+
+import _Login from './js/components/Login.js'
+import _Browse from './js/components/Browse.js'
+import fontAwesome from 'font-awesome/css/font-awesome.css'
+
+import Web from './js/web'
+window.Web = Web
+
+import storage from 'local-storage-fallback'
+
+const store = applyMiddleware(thunkMiddleware)(createStore)(reducer)
+const Browse = connect(state => state)(_Browse)
+const Login = connect(state => state)(_Login)
+
+let web = new Web(`${window.location.protocol}//${window.location.host}${minioBrowserPrefix}/webrpc`, store.dispatch)
+
+window.web = web
+
+store.dispatch(actions.setWeb(web))
+
+function authNeeded(nextState, replace, cb) {
+ if (web.LoggedIn()) {
+ return cb()
+ }
+ if (location.pathname === minioBrowserPrefix || location.pathname === minioBrowserPrefix + '/') {
+ replace(`${minioBrowserPrefix}/login`)
+ }
+ return cb()
+}
+
+function authNotNeeded(nextState, replace) {
+ if (web.LoggedIn()) {
+ replace(`${minioBrowserPrefix}`)
+ }
+}
+
+const App = (props) => {
+ return
+ )
+ }
+}
diff --git a/browser/app/js/components/BrowserDropdown.js b/browser/app/js/components/BrowserDropdown.js
new file mode 100644
index 000000000..1aa272551
--- /dev/null
+++ b/browser/app/js/components/BrowserDropdown.js
@@ -0,0 +1,56 @@
+/*
+ * Minio Browser (C) 2016, 2017 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import connect from 'react-redux/lib/components/connect'
+import Dropdown from 'react-bootstrap/lib/Dropdown'
+
+let BrowserDropdown = ({fullScreen, showAbout, showSettings, logout}) => {
+ return (
+
+ )
+}
+
+export default connect(state => state)(BrowserDropdown)
diff --git a/browser/app/js/components/BrowserUpdate.js b/browser/app/js/components/BrowserUpdate.js
new file mode 100644
index 000000000..be4c7af3a
--- /dev/null
+++ b/browser/app/js/components/BrowserUpdate.js
@@ -0,0 +1,42 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import connect from 'react-redux/lib/components/connect'
+
+import Tooltip from 'react-bootstrap/lib/Tooltip'
+import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'
+
+let BrowserUpdate = ({latestUiVersion}) => {
+ // Don't show an update if we're already updated!
+ if (latestUiVersion === currentUiVersion) return ( )
+
+ return (
+
+ )
+}
+
+export default connect(state => {
+ return {
+ latestUiVersion: state.latestUiVersion
+ }
+})(BrowserUpdate)
diff --git a/browser/app/js/components/ConfirmModal.js b/browser/app/js/components/ConfirmModal.js
new file mode 100644
index 000000000..fd98fa313
--- /dev/null
+++ b/browser/app/js/components/ConfirmModal.js
@@ -0,0 +1,50 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import Modal from 'react-bootstrap/lib/Modal'
+import ModalBody from 'react-bootstrap/lib/ModalBody'
+
+let ConfirmModal = ({baseClass, icon, text, sub, okText, cancelText, okHandler, cancelHandler, show}) => {
+ return (
+
+
+
+
+
+
+ { text }
+
+
+ { sub }
+
+
+
+
+
+
+
+ )
+}
+
+export default ConfirmModal
diff --git a/browser/app/js/components/Dropzone.js b/browser/app/js/components/Dropzone.js
new file mode 100644
index 000000000..0ddab2661
--- /dev/null
+++ b/browser/app/js/components/Dropzone.js
@@ -0,0 +1,65 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import ReactDropzone from 'react-dropzone'
+import * as actions from '../actions'
+
+// Dropzone is a drag-and-drop element for uploading files. It will create a
+// landing zone of sorts that automatically receives the files.
+export default class Dropzone extends React.Component {
+
+ onDrop(files) {
+ // FIXME: Currently you can upload multiple files, but only one abort
+ // modal will be shown, and progress updates will only occur for one
+ // file at a time. See #171.
+ files.forEach(file => {
+ let req = new XMLHttpRequest()
+
+ // Dispatch the upload.
+ web.dispatch(actions.uploadFile(file, req))
+ })
+ }
+
+ render() {
+ // Overwrite the default styling from react-dropzone; otherwise it
+ // won't handle child elements correctly.
+ const style = {
+ height: '100%',
+ borderWidth: '2px',
+ borderStyle: 'dashed',
+ borderColor: '#fff'
+ }
+ const activeStyle = {
+ borderColor: '#777'
+ }
+ const rejectStyle = {
+ backgroundColor: '#ffdddd'
+ }
+
+ // disableClick means that it won't trigger a file upload box when
+ // the user clicks on a file.
+ return (
+
+ { this.props.children }
+
+ )
+ }
+}
diff --git a/browser/app/js/components/InputGroup.js b/browser/app/js/components/InputGroup.js
new file mode 100644
index 000000000..c2b0e2ab2
--- /dev/null
+++ b/browser/app/js/components/InputGroup.js
@@ -0,0 +1,49 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+
+let InputGroup = ({label, id, name, value, onChange, type, spellCheck, required, readonly, autoComplete, align, className}) => {
+ var input =
+ if (readonly)
+ input =
+ return
+ { input }
+
+
+
+}
+
+export default InputGroup
diff --git a/browser/app/js/components/Login.js b/browser/app/js/components/Login.js
new file mode 100644
index 000000000..b5db1a872
--- /dev/null
+++ b/browser/app/js/components/Login.js
@@ -0,0 +1,133 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import classNames from 'classnames'
+import logo from '../../img/logo.svg'
+import Alert from 'react-bootstrap/lib/Alert'
+import * as actions from '../actions'
+import InputGroup from '../components/InputGroup'
+
+export default class Login extends React.Component {
+ handleSubmit(event) {
+ event.preventDefault()
+ const {web, dispatch, loginRedirectPath} = this.props
+ let message = ''
+ if (!document.getElementById('accessKey').value) {
+ message = 'Secret Key cannot be empty'
+ }
+ if (!document.getElementById('secretKey').value) {
+ message = 'Access Key cannot be empty'
+ }
+ if (message) {
+ dispatch(actions.showAlert({
+ type: 'danger',
+ message
+ }))
+ return
+ }
+ web.Login({
+ username: document.getElementById('accessKey').value,
+ password: document.getElementById('secretKey').value
+ })
+ .then((res) => {
+ this.context.router.push(loginRedirectPath)
+ })
+ .catch(e => {
+ dispatch(actions.setLoginError())
+ dispatch(actions.showAlert({
+ type: 'danger',
+ message: e.message
+ }))
+ })
+ }
+
+ componentWillMount() {
+ const {dispatch} = this.props
+ // Clear out any stale message in the alert of previous page
+ dispatch(actions.showAlert({
+ type: 'danger',
+ message: ''
+ }))
+ document.body.classList.add('is-guest')
+ }
+
+ componentWillUnmount() {
+ document.body.classList.remove('is-guest')
+ }
+
+ hideAlert() {
+ const {dispatch} = this.props
+ dispatch(actions.hideAlert())
+ }
+
+ render() {
+ const {alert} = this.props
+ let alertBox =
+
+ { alert.message }
+
+
+ // Make sure you don't show a fading out alert box on the initial web-page load.
+ if (!alert.message)
+ alertBox = ''
+ return (
+
+ { alertBox }
+
+
+
+
+
+
+ { window.location.host }
+
+
+
+ )
+ }
+}
+
+Login.contextTypes = {
+ router: React.PropTypes.object.isRequired
+}
diff --git a/browser/app/js/components/ObjectsList.js b/browser/app/js/components/ObjectsList.js
new file mode 100644
index 000000000..623594218
--- /dev/null
+++ b/browser/app/js/components/ObjectsList.js
@@ -0,0 +1,75 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import Moment from 'moment'
+import humanize from 'humanize'
+import connect from 'react-redux/lib/components/connect'
+import Dropdown from 'react-bootstrap/lib/Dropdown'
+
+
+let ObjectsList = ({objects, currentPath, selectPrefix, dataType, showDeleteConfirmation, shareObject, loadPath}) => {
+ const list = objects.map((object, i) => {
+ let size = object.name.endsWith('/') ? '-' : humanize.filesize(object.size)
+ let lastModified = object.name.endsWith('/') ? '-' : Moment(object.lastModified).format('lll')
+ let loadingClass = loadPath === `${currentPath}${object.name}` ? 'fesl-loading' : ''
+ let actionButtons = ''
+ let deleteButton = ''
+ if (web.LoggedIn())
+ deleteButton = showDeleteConfirmation(e, `${currentPath}${object.name}`) }>
+ if (!object.name.endsWith('/')) {
+ actionButtons =
+
+
+ shareObject(e, `${currentPath}${object.name}`) }>
+ { deleteButton }
+
+
+ }
+ return (
+
+ )
+}
+
+// Subscribe it to state changes.
+export default connect(state => {
+ return {
+ objects: state.objects,
+ currentPath: state.currentPath,
+ loadPath: state.loadPath
+ }
+})(ObjectsList)
diff --git a/browser/app/js/components/Path.js b/browser/app/js/components/Path.js
new file mode 100644
index 000000000..6ca85869b
--- /dev/null
+++ b/browser/app/js/components/Path.js
@@ -0,0 +1,41 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import connect from 'react-redux/lib/components/connect'
+
+let Path = ({currentBucket, currentPath, selectPrefix}) => {
+ let dirPath = []
+ let path = ''
+ if (currentPath) {
+ path = currentPath.split('/').map((dir, i) => {
+ dirPath.push(dir)
+ let dirPath_ = dirPath.join('/') + '/'
+ return selectPrefix(e, dirPath_) }>{ dir }
+ })
+ }
+
+ return (
+
+
+ )
+ }
+}
+
+export default connect(state => state)(PolicyInput)
diff --git a/browser/app/js/components/SettingsModal.js b/browser/app/js/components/SettingsModal.js
new file mode 100644
index 000000000..51bd4333b
--- /dev/null
+++ b/browser/app/js/components/SettingsModal.js
@@ -0,0 +1,215 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import connect from 'react-redux/lib/components/connect'
+import * as actions from '../actions'
+
+import Tooltip from 'react-bootstrap/lib/Tooltip'
+import Modal from 'react-bootstrap/lib/Modal'
+import ModalBody from 'react-bootstrap/lib/ModalBody'
+import ModalHeader from 'react-bootstrap/lib/ModalHeader'
+import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'
+import InputGroup from './InputGroup'
+
+class SettingsModal extends React.Component {
+
+ // When the settings are shown, it loads the access key and secret key.
+ componentWillMount() {
+ const {web, dispatch} = this.props
+ const {serverInfo} = this.props
+
+ let accessKeyEnv = ''
+ let secretKeyEnv = ''
+ // Check environment variables first. They may or may not have been
+ // loaded already; they load in Browse#componentDidMount.
+ if (serverInfo.envVars) {
+ serverInfo.envVars.forEach(envVar => {
+ let keyVal = envVar.split('=')
+ if (keyVal[0] == 'MINIO_ACCESS_KEY') {
+ accessKeyEnv = keyVal[1]
+ } else if (keyVal[0] == 'MINIO_SECRET_KEY') {
+ secretKeyEnv = keyVal[1]
+ }
+ })
+ }
+ if (accessKeyEnv != '' || secretKeyEnv != '') {
+ dispatch(actions.setSettings({
+ accessKey: accessKeyEnv,
+ secretKey: secretKeyEnv,
+ keysReadOnly: true
+ }))
+ } else {
+ web.GetAuth()
+ .then(data => {
+ dispatch(actions.setSettings({
+ accessKey: data.accessKey,
+ secretKey: data.secretKey
+ }))
+ })
+ }
+ }
+
+ // When they are re-hidden, the keys are unloaded from memory.
+ componentWillUnmount() {
+ const {dispatch} = this.props
+
+ dispatch(actions.setSettings({
+ accessKey: '',
+ secretKey: '',
+ secretKeyVisible: false
+ }))
+ dispatch(actions.hideSettings())
+ }
+
+ // Handle field changes from inside the modal.
+ accessKeyChange(e) {
+ const {dispatch} = this.props
+ dispatch(actions.setSettings({
+ accessKey: e.target.value
+ }))
+ }
+
+ secretKeyChange(e) {
+ const {dispatch} = this.props
+ dispatch(actions.setSettings({
+ secretKey: e.target.value
+ }))
+ }
+
+ secretKeyVisible(secretKeyVisible) {
+ const {dispatch} = this.props
+ dispatch(actions.setSettings({
+ secretKeyVisible
+ }))
+ }
+
+ // Save the auth params and set them.
+ setAuth(e) {
+ e.preventDefault()
+ const {web, dispatch} = this.props
+
+ let accessKey = document.getElementById('accessKey').value
+ let secretKey = document.getElementById('secretKey').value
+ web.SetAuth({
+ accessKey,
+ secretKey
+ })
+ .then(data => {
+ dispatch(actions.setSettings({
+ accessKey: '',
+ secretKey: '',
+ secretKeyVisible: false
+ }))
+ dispatch(actions.hideSettings())
+ dispatch(actions.showAlert({
+ type: 'success',
+ message: 'Changed credentials'
+ }))
+ })
+ .catch(err => {
+ dispatch(actions.setSettings({
+ accessKey: '',
+ secretKey: '',
+ secretKeyVisible: false
+ }))
+ dispatch(actions.hideSettings())
+ dispatch(actions.showAlert({
+ type: 'danger',
+ message: err.message
+ }))
+ })
+ }
+
+ generateAuth(e) {
+ e.preventDefault()
+ const {dispatch} = this.props
+
+ web.GenerateAuth()
+ .then(data => {
+ dispatch(actions.setSettings({
+ secretKeyVisible: true
+ }))
+ dispatch(actions.setSettings({
+ accessKey: data.accessKey,
+ secretKey: data.secretKey
+ }))
+ })
+ }
+
+ hideSettings(e) {
+ e.preventDefault()
+
+ const {dispatch} = this.props
+ dispatch(actions.hideSettings())
+ }
+
+ render() {
+ let {settings} = this.props
+
+ return (
+
+
+ Change Password
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default connect(state => {
+ return {
+ web: state.web,
+ settings: state.settings,
+ serverInfo: state.serverInfo
+ }
+})(SettingsModal)
diff --git a/browser/app/js/components/SideBar.js b/browser/app/js/components/SideBar.js
new file mode 100644
index 000000000..ad4aee576
--- /dev/null
+++ b/browser/app/js/components/SideBar.js
@@ -0,0 +1,85 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import classNames from 'classnames'
+import ClickOutHandler from 'react-onclickout'
+import Scrollbars from 'react-custom-scrollbars/lib/Scrollbars'
+import connect from 'react-redux/lib/components/connect'
+
+import logo from '../../img/logo.svg'
+
+let SideBar = ({visibleBuckets, loadBucket, currentBucket, selectBucket, searchBuckets, landingPage, sidebarStatus, clickOutside, showPolicy}) => {
+
+ const list = visibleBuckets.map((bucket, i) => {
+ return
+
+ )
+}
+
+// Subscribe it to state changes that affect only the sidebar.
+export default connect(state => {
+ return {
+ visibleBuckets: state.visibleBuckets,
+ loadBucket: state.loadBucket,
+ currentBucket: state.currentBucket,
+ sidebarStatus: state.sidebarStatus
+ }
+})(SideBar)
diff --git a/browser/app/js/components/UploadModal.js b/browser/app/js/components/UploadModal.js
new file mode 100644
index 000000000..6658ab225
--- /dev/null
+++ b/browser/app/js/components/UploadModal.js
@@ -0,0 +1,141 @@
+/*
+ * Minio Browser (C) 2016 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import humanize from 'humanize'
+import classNames from 'classnames'
+import connect from 'react-redux/lib/components/connect'
+
+import ProgressBar from 'react-bootstrap/lib/ProgressBar'
+import ConfirmModal from './ConfirmModal'
+
+import * as actions from '../actions'
+
+// UploadModal is a modal that handles multiple file uploads.
+// During the upload, it displays a progress bar, and can transform into an
+// abort modal if the user decides to abort the uploads.
+class UploadModal extends React.Component {
+
+ // Abort all the current uploads.
+ abortUploads(e) {
+ e.preventDefault()
+ const {dispatch, uploads} = this.props
+
+ for (var slug in uploads) {
+ let upload = uploads[slug]
+ upload.xhr.abort()
+ dispatch(actions.stopUpload({
+ slug
+ }))
+ }
+
+ this.hideAbort(e)
+ }
+
+ // Show the abort modal instead of the progress modal.
+ showAbort(e) {
+ e.preventDefault()
+ const {dispatch} = this.props
+
+ dispatch(actions.setShowAbortModal(true))
+ }
+
+ // Show the progress modal instead of the abort modal.
+ hideAbort(e) {
+ e.preventDefault()
+ const {dispatch} = this.props
+
+ dispatch(actions.setShowAbortModal(false))
+ }
+
+ render() {
+ const {uploads, showAbortModal} = this.props
+
+ // Show the abort modal.
+ if (showAbortModal) {
+ let baseClass = classNames({
+ 'abort-upload': true
+ })
+ let okIcon = classNames({
+ 'fa': true,
+ 'fa-times': true
+ })
+ let cancelIcon = classNames({
+ 'fa': true,
+ 'fa-cloud-upload': true
+ })
+
+ return (
+
+
+ )
+ }
+
+ // If we don't have any files uploading, don't show anything.
+ let numberUploading = Object.keys(uploads).length
+ if (numberUploading == 0)
+ return ( )
+
+ let totalLoaded = 0
+ let totalSize = 0
+
+ // Iterate over each upload, adding together the total size and that
+ // which has been uploaded.
+ for (var slug in uploads) {
+ let upload = uploads[slug]
+ totalLoaded += upload.loaded
+ totalSize += upload.size
+ }
+
+ let percent = (totalLoaded / totalSize) * 100
+
+ // If more than one: "Uploading files (5)..."
+ // If only one: "Uploading myfile.txt..."
+ let text = 'Uploading ' + (numberUploading == 1 ? `'${uploads[Object.keys(uploads)[0]].name}'` : `files (${numberUploading})`) + '...'
+
+ return (
+