Skip to content

Commit 829d6df

Browse files
authored
Merge pull request #205 from CodeForSocialGood/featura/minio-server
Minio-server and upload project image
2 parents ee7ae1b + b99ff67 commit 829d6df

File tree

13 files changed

+256
-32
lines changed

13 files changed

+256
-32
lines changed

.setup/minio/run.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
3+
set +x -e -o pipefail
4+
5+
NAME=minio
6+
ID_FROM_NAME=$(docker ps -aq -f name=$NAME)
7+
IMAGE=minio/minio
8+
9+
start () {
10+
set -u
11+
# Make sure a container isn't already running
12+
if [[ -z "$ID_FROM_NAME" ]] ; then
13+
echo "--- Starting new docker container with Minio.."
14+
docker run --name=$NAME -d -p 9000:9000 \
15+
-e "MINIO_ACCESS_KEY=minioAccessKey" \
16+
-e "MINIO_SECRET_KEY=minioSecretKey" \
17+
$IMAGE server /data \
18+
| xargs echo "--- Started container"
19+
else
20+
echo "--- Skipping start, container already running"
21+
exit 0
22+
fi
23+
}
24+
25+
stop () {
26+
set -u
27+
# Make sure there's a container to stop
28+
if [[ ! -z "$ID_FROM_NAME" ]] ; then
29+
ID=$ID_FROM_NAME
30+
else
31+
echo "--- Skipping stop and remove, no container found"
32+
exit 0
33+
fi
34+
35+
echo "--- Stopping and removing docker container.."
36+
docker stop $ID | xargs echo "--- Stopped container"
37+
docker rm $ID | xargs echo "--- Removed container"
38+
}
39+
40+
info () {
41+
cat <<EOF
42+
Usage: ./minio/run.sh <target>
43+
Targets:
44+
start - start a docker container with minio content server
45+
stop - stop and remove the docker container
46+
EOF
47+
}
48+
49+
case $1 in
50+
start) start ;;
51+
stop) stop ;;
52+
*) info ;;
53+
esac

client/actions/project/index.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import {
2-
FETCHING_PROJECTS,
3-
RECEIVED_PROJECTS,
4-
FAILED_PROJECTS
5-
} from './types'
1+
import * as actionTypes from './types'
2+
import { push } from 'react-router-redux'
63

74
import apiOptionsFromState from '../../api/lib/apiOptionsFromState'
85
import projectsApiClient from '../../api/projects'
96

10-
export const fetching = { type: FETCHING_PROJECTS }
11-
export const received = { type: RECEIVED_PROJECTS }
12-
export const failed = { type: FAILED_PROJECTS }
7+
export const fetching = { type: actionTypes.FETCHING_PROJECTS }
8+
export const received = { type: actionTypes.RECEIVED_PROJECTS }
9+
export const failed = { type: actionTypes.FAILED_PROJECTS }
1310

1411
export default class ProjectActionCreator {
1512
static fetching () {
@@ -81,4 +78,31 @@ export default class ProjectActionCreator {
8178
}
8279
}
8380
}
81+
82+
static async uploadImage (file, apiOptions) {
83+
const url = await projectsApiClient.getPresignedUrlForProjectImage(apiOptions, file.name)
84+
await projectsApiClient.uploadImage(apiOptions, url, file)
85+
}
86+
87+
static createProject (projectName, causes, technologies, organization, file) {
88+
return async (dispatch, getState) => {
89+
dispatch({type: actionTypes.CREATE_PROJECT_START})
90+
91+
try {
92+
const state = getState()
93+
const apiOptions = apiOptionsFromState(state)
94+
await ProjectActionCreator.uploadImage(file, apiOptions)
95+
await projectsApiClient.createProject(apiOptions, projectName, causes,
96+
technologies, organization)
97+
dispatch({type: actionTypes.CREATE_PROJECT_SUCCESS})
98+
dispatch(push('/'))
99+
} catch (e) {
100+
console.trace(e)
101+
dispatch({
102+
type: actionTypes.CREATE_PROJECT_FAILED,
103+
payload: e,
104+
error: true})
105+
}
106+
}
107+
}
84108
}

client/actions/project/types.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
export const FETCHING_PROJECTS = 'FETCHING_PROJECTS'
22
export const RECEIVED_PROJECTS = 'RECEIVED_PROJECTS'
33
export const FAILED_PROJECTS = 'FAILED_PROJECTS'
4+
5+
export const CREATE_PROJECT_START = 'CREATE_PROJECT_START'
6+
export const CREATE_PROJECT_FAILED = 'CREATE_PROJECT_FAILED'
7+
export const CREATE_PROJECT_SUCCESS = 'CREATE_PROJECT_SUCCESS'

client/api/projects.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const projectsApiClient = {
1515
return apiRequest.get('/projects', apiOptions, query)
1616
},
1717

18-
async createProject (name, causes, technologies, organization) {
18+
async createProject (apiOptions, name, causes, technologies, organization) {
1919
const options = {
2020
method: 'POST',
2121
headers: {
@@ -26,6 +26,18 @@ const projectsApiClient = {
2626
return fetch('/api/projects/', options)
2727
},
2828

29+
async getPresignedUrlForProjectImage (apiOptions, imageName) {
30+
const query = { imageName }
31+
return apiRequest.get('/projects/presignedUrl', apiOptions, query)
32+
},
33+
34+
async uploadImage (apiOptions, url, file) {
35+
return fetch(url, {
36+
method: 'PUT',
37+
body: file
38+
})
39+
},
40+
2941
getProjectById (apiOptions, id) {
3042
return apiRequest.get('/projects/' + id, apiOptions, null)
3143
}

client/components/CreateProjectForm/CreateProjectForm.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import Button from 'material-ui/Button/Button'
77
import Checkbox from 'material-ui/Checkbox'
88
import { FormGroup, FormLabel, FormControlLabel } from 'material-ui/Form'
99

10-
import projectsApiClient from '../../api/projects'
1110
import UploadDropzone from '../UploadDropzone/UploadDropzone'
1211
import styles from './CreateProjectForm.scss'
1312
import { buttonSubmit } from './createProjectFormJss'
1413
import { causes, technologies } from '../shared/constants'
1514

15+
import ProjectActionCreator from '../../actions/project/'
16+
1617
class CreateProjectForm extends Component {
1718
constructor (props, context) {
1819
super(props, context)
@@ -25,12 +26,13 @@ class CreateProjectForm extends Component {
2526
error: '',
2627
projectName: '',
2728
causes: [],
28-
technologies: []
29+
technologies: [],
30+
file: ''
2931
}
3032
}
3133

32-
saveFile () {
33-
// TODO ... implement the save file
34+
saveFile (file) {
35+
this.setState({file})
3436
}
3537

3638
projectNameChange (event) {
@@ -40,17 +42,11 @@ class CreateProjectForm extends Component {
4042

4143
async createProject (event) {
4244
event.preventDefault()
43-
const response = await projectsApiClient.createProject(
44-
this.state.projectName,
45+
this.props.createProject(this.state.projectName,
4546
this.state.causes,
4647
this.state.technologies,
47-
this.props.user.organization
48-
)
49-
if (response.status === 500) {
50-
this.setState({error: response.statusText})
51-
} else {
52-
this.context.router.history.push('/profile')
53-
}
48+
this.props.user.organization,
49+
this.state.file)
5450
}
5551

5652
handleCheckbox (event, checked) {
@@ -104,9 +100,9 @@ class CreateProjectForm extends Component {
104100
Create Project
105101
</Button>
106102

107-
{this.state.error &&
103+
{this.props.errorMessage.length > 0 &&
108104
<div className={styles.errorContent}>
109-
{this.state.error}
105+
{this.props.errorMessage}
110106
</div>
111107
}
112108
</form>
@@ -117,10 +113,15 @@ class CreateProjectForm extends Component {
117113
function mapStateToProps (state) {
118114
return {
119115
projects: state.project.projects,
120-
user: state.user
116+
user: state.user,
117+
errorMessage: state.project.error.message
121118
}
122119
}
123120

121+
const mapDispatchToProps = {
122+
createProject: ProjectActionCreator.createProject
123+
}
124+
124125
CreateProjectForm.contextTypes = {
125126
router: PropTypes.object
126127
}
@@ -130,7 +131,9 @@ CreateProjectForm.propTypes = {
130131
handleSubmit: PropTypes.func,
131132
createProject: PropTypes.func,
132133
user: PropTypes.object,
133-
classes: PropTypes.object
134+
classes: PropTypes.object,
135+
projects: PropTypes.array,
136+
errorMessage: PropTypes.string
134137
}
135138

136-
export default connect(mapStateToProps, null)(withStyles(buttonSubmit)(CreateProjectForm))
139+
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(buttonSubmit)(CreateProjectForm))

client/reducers/initialState.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export default {
99
},
1010
projects: {
1111
fetching: false,
12-
projects: []
12+
projects: [],
13+
error: {
14+
message: ''
15+
}
1316
},
1417
users: {},
1518
applications: {

client/reducers/projectReducer.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import initialState from './initialState'
22
import {
33
FETCHING_PROJECTS,
44
RECEIVED_PROJECTS,
5-
FAILED_PROJECTS
5+
FAILED_PROJECTS,
6+
CREATE_PROJECT_FAILED
67
} from '../actions/project/types'
78

89
export default function (state = initialState.projects, action) {
@@ -22,6 +23,9 @@ export default function (state = initialState.projects, action) {
2223

2324
case FAILED_PROJECTS:
2425
return { ...state, fetching: false }
26+
case CREATE_PROJECT_FAILED: {
27+
return {...state, error: {message: payload}}
28+
}
2529

2630
default:
2731
return state

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
"client:watch": "webpack --mode development --watch --progress --hide-modules",
1414
"server:watch": "nodemon --watch server/ --ignore server/public/ --exec 'node server/'",
1515
"sync": "browser-sync start --proxy localhost:3000 --files server/**/* --port 3001 --ws --no-open --https",
16-
"prestart": "yarn db start && yarn email start && yarn clear-ports",
16+
"prestart": "yarn db start && yarn email start && yarn minio start && yarn clear-ports",
1717
"start": "npm-run-all --parallel client:watch server:watch sync",
18-
"stop": "yarn db stop && yarn email stop",
18+
"stop": "yarn db stop && yarn email stop && yarn minio stop",
1919
"restart": "yarn stop && yarn start",
2020
"pretest": "yarn lint && yarn build",
2121
"test": "SILENT=true ava --verbose",
@@ -25,6 +25,7 @@
2525
"build": "webpack --mode production --bail --display errors-only",
2626
"db": "./.setup/db/run.sh",
2727
"email": "./.setup/email/run.sh",
28+
"minio": "./.setup/minio/run.sh",
2829
"clear-ports": "./.setup/client/clear-ports.sh"
2930
},
3031
"dependencies": {
@@ -93,6 +94,7 @@
9394
"eslint-plugin-react": "^7.7.0",
9495
"eslint-plugin-standard": "^3.0.1",
9596
"ignore-styles": "^5.0.1",
97+
"minio": "^5.0.0",
9698
"mongodb-memory-server": "^1.6.3",
9799
"nightwatch": "^1.0.1",
98100
"nightwatch-cucumber": "^9.0.0",

server/config/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export { default as appConfig } from './app'
22
export { default as authConfig } from './auth'
33
export { default as databaseConfig } from './database'
44
export { default as mailerConfig } from './mailer'
5+
export { default as minioConfig } from './minio'
56
export { default as rollbarConfig } from './rollbar'

server/config/minio.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Client } from 'minio'
2+
3+
const region = 'us-east-1'
4+
5+
const minioClient = new Client({
6+
endPoint: 'localhost',
7+
port: 9000,
8+
path: 'minio',
9+
secure: false,
10+
insecure: true,
11+
accessKey: 'minioAccessKey',
12+
secretKey: 'minioSecretKey'
13+
})
14+
15+
export default {
16+
client: minioClient,
17+
region
18+
}

0 commit comments

Comments
 (0)