Этот коммит содержится в:
=Corey Hulen
2015-09-21 17:34:13 -07:00
родитель e863096358
Коммит 0529a494ae
12 изменённых файлов: 524 добавлений и 98 удалений

Просмотреть файл

@@ -278,11 +278,11 @@ func copyDirToExportWriter(writer ExportWriter, inPath string, outPath string) *
} }
func ExportLocalStorage(writer ExportWriter, options *ExportOptions, teamId string) *model.AppError { func ExportLocalStorage(writer ExportWriter, options *ExportOptions, teamId string) *model.AppError {
teamDir := utils.Cfg.ServiceSettings.StorageDirectory + "teams/" + teamId teamDir := utils.Cfg.ImageSettings.Directory + "teams/" + teamId
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_S3 {
return model.NewAppError("ExportLocalStorage", "S3 is not supported for local storage export.", "") return model.NewAppError("ExportLocalStorage", "S3 is not supported for local storage export.", "")
} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 { } else if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := copyDirToExportWriter(writer, teamDir, EXPORT_LOCAL_STORAGE_FOLDER); err != nil { if err := copyDirToExportWriter(writer, teamDir, EXPORT_LOCAL_STORAGE_FOLDER); err != nil {
return err return err
} }

Просмотреть файл

@@ -68,8 +68,8 @@ func InitFile(r *mux.Router) {
} }
func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if len(utils.Cfg.ImageSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadFile", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "") c.Err = model.NewAppError("uploadFile", "Unable to upload file. Image storage is not configured.", "")
c.Err.StatusCode = http.StatusNotImplemented c.Err.StatusCode = http.StatusNotImplemented
return return
} }
@@ -293,8 +293,8 @@ type ImageGetResult struct {
} }
func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if len(utils.Cfg.ImageSettings.DriverName) == 0 {
c.Err = model.NewAppError("getFileInfo", "Unable to get file info. Amazon S3 not configured and local server storage turned off. ", "") c.Err = model.NewAppError("uploadFile", "Unable to get file info. Image storage is not configured.", "")
c.Err.StatusCode = http.StatusNotImplemented c.Err.StatusCode = http.StatusNotImplemented
return return
} }
@@ -356,8 +356,8 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
} }
func getFile(c *Context, w http.ResponseWriter, r *http.Request) { func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if len(utils.Cfg.ImageSettings.DriverName) == 0 {
c.Err = model.NewAppError("getFile", "Unable to get file. Amazon S3 not configured and local server storage turned off. ", "") c.Err = model.NewAppError("uploadFile", "Unable to get file. Image storage is not configured.", "")
c.Err.StatusCode = http.StatusNotImplemented c.Err.StatusCode = http.StatusNotImplemented
return return
} }
@@ -441,17 +441,17 @@ func asyncGetFile(path string, fileData chan []byte) {
} }
func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
if len(utils.Cfg.ImageSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadFile", "Unable to get link. Image storage is not configured.", "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if !utils.Cfg.TeamSettings.AllowPublicLink { if !utils.Cfg.TeamSettings.AllowPublicLink {
c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "") c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "")
c.Err.StatusCode = http.StatusForbidden c.Err.StatusCode = http.StatusForbidden
} }
if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
c.Err = model.NewAppError("getPublicLink", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
props := model.MapFromJson(r.Body) props := model.MapFromJson(r.Body)
filename := props["filename"] filename := props["filename"]
@@ -510,13 +510,13 @@ func getExport(c *Context, w http.ResponseWriter, r *http.Request) {
func writeFile(f []byte, path string) *model.AppError { func writeFile(f []byte, path string) *model.AppError {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_S3 {
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.ImageSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.ImageSettings.AmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.ImageSettings.AmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.ImageSettings.AmazonS3Bucket)
ext := filepath.Ext(path) ext := filepath.Ext(path)
@@ -533,12 +533,12 @@ func writeFile(f []byte, path string) *model.AppError {
if err != nil { if err != nil {
return model.NewAppError("writeFile", "Encountered an error writing to S3", err.Error()) return model.NewAppError("writeFile", "Encountered an error writing to S3", err.Error())
} }
} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 { } else if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := os.MkdirAll(filepath.Dir(utils.Cfg.ServiceSettings.StorageDirectory+path), 0774); err != nil { if err := os.MkdirAll(filepath.Dir(utils.Cfg.ImageSettings.Directory+path), 0774); err != nil {
return model.NewAppError("writeFile", "Encountered an error creating the directory for the new file", err.Error()) return model.NewAppError("writeFile", "Encountered an error creating the directory for the new file", err.Error())
} }
if err := ioutil.WriteFile(utils.Cfg.ServiceSettings.StorageDirectory+path, f, 0644); err != nil { if err := ioutil.WriteFile(utils.Cfg.ImageSettings.Directory+path, f, 0644); err != nil {
return model.NewAppError("writeFile", "Encountered an error writing to local server storage", err.Error()) return model.NewAppError("writeFile", "Encountered an error writing to local server storage", err.Error())
} }
} else { } else {
@@ -550,13 +550,13 @@ func writeFile(f []byte, path string) *model.AppError {
func readFile(path string) ([]byte, *model.AppError) { func readFile(path string) ([]byte, *model.AppError) {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_S3 {
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.ImageSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.ImageSettings.AmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.ImageSettings.AmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.ImageSettings.AmazonS3Bucket)
// try to get the file from S3 with some basic retry logic // try to get the file from S3 with some basic retry logic
tries := 0 tries := 0
@@ -572,8 +572,8 @@ func readFile(path string) ([]byte, *model.AppError) {
} }
time.Sleep(3000 * time.Millisecond) time.Sleep(3000 * time.Millisecond)
} }
} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 { } else if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if f, err := ioutil.ReadFile(utils.Cfg.ServiceSettings.StorageDirectory + path); err != nil { if f, err := ioutil.ReadFile(utils.Cfg.ImageSettings.Directory + path); err != nil {
return nil, model.NewAppError("readFile", "Encountered an error reading from local server storage", err.Error()) return nil, model.NewAppError("readFile", "Encountered an error reading from local server storage", err.Error())
} else { } else {
return f, nil return f, nil
@@ -584,14 +584,14 @@ func readFile(path string) ([]byte, *model.AppError) {
} }
func openFileWriteStream(path string) (io.Writer, *model.AppError) { func openFileWriteStream(path string) (io.Writer, *model.AppError) {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_S3 {
return nil, model.NewAppError("openFileWriteStream", "S3 is not supported.", "") return nil, model.NewAppError("openFileWriteStream", "S3 is not supported.", "")
} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 { } else if utils.Cfg.ImageSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := os.MkdirAll(filepath.Dir(utils.Cfg.ServiceSettings.StorageDirectory+path), 0774); err != nil { if err := os.MkdirAll(filepath.Dir(utils.Cfg.ImageSettings.Directory+path), 0774); err != nil {
return nil, model.NewAppError("openFileWriteStream", "Encountered an error creating the directory for the new file", err.Error()) return nil, model.NewAppError("openFileWriteStream", "Encountered an error creating the directory for the new file", err.Error())
} }
if fileHandle, err := os.Create(utils.Cfg.ServiceSettings.StorageDirectory + path); err != nil { if fileHandle, err := os.Create(utils.Cfg.ImageSettings.Directory + path); err != nil {
return nil, model.NewAppError("openFileWriteStream", "Encountered an error writing to local server storage", err.Error()) return nil, model.NewAppError("openFileWriteStream", "Encountered an error writing to local server storage", err.Error())
} else { } else {
fileHandle.Chmod(0644) fileHandle.Chmod(0644)

Просмотреть файл

@@ -81,11 +81,11 @@ func TestUploadFile(t *testing.T) {
fileId := strings.Split(filename, ".")[0] fileId := strings.Split(filename, ".")[0]
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.AWSSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.AWSSettingsAmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettingsAmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.AWSSettingsAmazonS3Bucket)
// wait a bit for files to ready // wait a bit for files to ready
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
@@ -264,11 +264,11 @@ func TestGetFile(t *testing.T) {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.AWSSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.AWSSettingsAmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettingsAmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.AWSSettingsAmazonS3Bucket)
filenames := strings.Split(resp.Data.(*model.FileUploadResponse).Filenames[0], "/") filenames := strings.Split(resp.Data.(*model.FileUploadResponse).Filenames[0], "/")
filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1] filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1]
@@ -413,11 +413,11 @@ func TestGetPublicLink(t *testing.T) {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
// perform clean-up on s3 // perform clean-up on s3
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.AWSSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.AWSSettingsAmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettingsAmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.AWSSettingsAmazonS3Bucket)
filenames := strings.Split(resp.Data.(*model.FileUploadResponse).Filenames[0], "/") filenames := strings.Split(resp.Data.(*model.FileUploadResponse).Filenames[0], "/")
filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1] filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1]

Просмотреть файл

@@ -704,7 +704,7 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
} else { } else {
var img []byte var img []byte
if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if len(utils.Cfg.ImageSettings.DriverName) == 0 {
var err *model.AppError var err *model.AppError
if img, err = createProfileImage(result.Data.(*model.User).Username, id); err != nil { if img, err = createProfileImage(result.Data.(*model.User).Username, id); err != nil {
c.Err = err c.Err = err
@@ -741,8 +741,8 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
} }
func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if len(utils.Cfg.ImageSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadProfileImage", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "") c.Err = model.NewAppError("uploadProfileImage", "Unable to upload file. Image storage is not configured.", "")
c.Err.StatusCode = http.StatusNotImplemented c.Err.StatusCode = http.StatusNotImplemented
return return
} }

Просмотреть файл

@@ -354,11 +354,11 @@ func TestUserCreateImage(t *testing.T) {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.AWSSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.AWSSettingsAmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettingsAmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.AWSSettingsAmazonS3Bucket)
if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil { if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -452,11 +452,11 @@ func TestUserUploadProfileImage(t *testing.T) {
if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage { if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
var auth aws.Auth var auth aws.Auth
auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId auth.AccessKey = utils.Cfg.AWSSettings.AmazonS3AccessKeyId
auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey auth.SecretKey = utils.Cfg.AWSSettingsAmazonS3SecretAccessKey
s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region]) s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettingsAmazonS3Region])
bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket) bucket := s.Bucket(utils.Cfg.AWSSettingsAmazonS3Bucket)
if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil { if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil {
t.Fatal(err) t.Fatal(err)

Просмотреть файл

@@ -18,8 +18,6 @@
"PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4", "PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4",
"ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t", "ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t",
"AnalyticsUrl": "", "AnalyticsUrl": "",
"UseLocalStorage": true,
"StorageDirectory": "./data/",
"AllowedLoginAttempts": 10, "AllowedLoginAttempts": 10,
"EnableOAuthServiceProvider": false "EnableOAuthServiceProvider": false
}, },
@@ -34,20 +32,20 @@
"Trace": false, "Trace": false,
"AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS"
}, },
"AWSSettings": {
"S3AccessKeyId": "",
"S3SecretAccessKey": "",
"S3Bucket": "",
"S3Region": ""
},
"ImageSettings": { "ImageSettings": {
"DriverName": "local",
"Directory": "./data/",
"ThumbnailWidth": 120, "ThumbnailWidth": 120,
"ThumbnailHeight": 100, "ThumbnailHeight": 100,
"PreviewWidth": 1024, "PreviewWidth": 1024,
"PreviewHeight": 0, "PreviewHeight": 0,
"ProfileWidth": 128, "ProfileWidth": 128,
"ProfileHeight": 128, "ProfileHeight": 128,
"InitialFont": "luximbi.ttf" "InitialFont": "luximbi.ttf",
"AmazonS3AccessKeyId": "",
"AmazonS3SecretAccessKey": "",
"AmazonS3Bucket": "",
"AmazonS3Region": ""
}, },
"EmailSettings": { "EmailSettings": {
"AllowSignUpWithEmail": true, "AllowSignUpWithEmail": true,

Просмотреть файл

@@ -8,6 +8,15 @@ import (
"io" "io"
) )
const (
CONN_SECURITY_NONE = ""
CONN_SECURITY_TLS = "TLS"
CONN_SECURITY_STARTTLS = "STARTTLS"
IMAGE_DRIVER_LOCAL = "local"
IMAGE_DRIVER_S3 = "amazons3"
)
type ServiceSettings struct { type ServiceSettings struct {
SiteName string SiteName string
Mode string Mode string
@@ -19,8 +28,6 @@ type ServiceSettings struct {
PublicLinkSalt string PublicLinkSalt string
ResetSalt string ResetSalt string
AnalyticsUrl string AnalyticsUrl string
UseLocalStorage bool
StorageDirectory string
AllowedLoginAttempts int AllowedLoginAttempts int
EnableOAuthServiceProvider bool EnableOAuthServiceProvider bool
} }
@@ -54,21 +61,20 @@ type LogSettings struct {
FileLocation string FileLocation string
} }
type AWSSettings struct {
S3AccessKeyId string
S3SecretAccessKey string
S3Bucket string
S3Region string
}
type ImageSettings struct { type ImageSettings struct {
ThumbnailWidth uint DriverName string
ThumbnailHeight uint Directory string
PreviewWidth uint ThumbnailWidth uint
PreviewHeight uint ThumbnailHeight uint
ProfileWidth uint PreviewWidth uint
ProfileHeight uint PreviewHeight uint
InitialFont string ProfileWidth uint
ProfileHeight uint
InitialFont string
AmazonS3AccessKeyId string
AmazonS3SecretAccessKey string
AmazonS3Bucket string
AmazonS3Region string
} }
type EmailSettings struct { type EmailSettings struct {
@@ -123,7 +129,6 @@ type Config struct {
LogSettings LogSettings LogSettings LogSettings
ServiceSettings ServiceSettings ServiceSettings ServiceSettings
SqlSettings SqlSettings SqlSettings SqlSettings
AWSSettings AWSSettings
ImageSettings ImageSettings ImageSettings ImageSettings
EmailSettings EmailSettings EmailSettings EmailSettings
RateLimitSettings RateLimitSettings RateLimitSettings RateLimitSettings

Просмотреть файл

@@ -198,13 +198,13 @@ func getClientProperties(c *model.Config) map[string]string {
return props return props
} }
func IsS3Configured() bool { // func IsS3Configured() bool {
if Cfg.AWSSettings.S3AccessKeyId == "" || Cfg.AWSSettings.S3SecretAccessKey == "" || Cfg.AWSSettings.S3Region == "" || Cfg.AWSSettings.S3Bucket == "" { // if Cfg.AWSSettings.AmazonS3AccessKeyId == "" || Cfg.AWSSettingsAmazonS3SecretAccessKey == "" || Cfg.AWSSettingsAmazonS3Region == "" || Cfg.AWSSettingsAmazonS3Bucket == "" {
return false // return false
} // }
return true // return true
} // }
func IsServiceAllowed(s string) bool { func IsServiceAllowed(s string) bool {
if len(s) == 0 { if len(s) == 0 {

Просмотреть файл

@@ -15,17 +15,11 @@ import (
"time" "time"
) )
const (
CONN_SECURITY_NONE = ""
CONN_SECURITY_TLS = "TLS"
CONN_SECURITY_STARTTLS = "STARTTLS"
)
func connectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) { func connectToSMTPServer(config *model.Config) (net.Conn, *model.AppError) {
var conn net.Conn var conn net.Conn
var err error var err error
if config.EmailSettings.ConnectionSecurity == CONN_SECURITY_TLS { if config.EmailSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
tlsconfig := &tls.Config{ tlsconfig := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
ServerName: config.EmailSettings.SMTPServer, ServerName: config.EmailSettings.SMTPServer,
@@ -54,11 +48,11 @@ func newSMTPClient(conn net.Conn, config *model.Config) (*smtp.Client, *model.Ap
// GO does not support plain auth over a non encrypted connection. // GO does not support plain auth over a non encrypted connection.
// so if not tls then no auth // so if not tls then no auth
auth := smtp.PlainAuth("", config.EmailSettings.SMTPUsername, config.EmailSettings.SMTPPassword, config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort) auth := smtp.PlainAuth("", config.EmailSettings.SMTPUsername, config.EmailSettings.SMTPPassword, config.EmailSettings.SMTPServer+":"+config.EmailSettings.SMTPPort)
if config.EmailSettings.ConnectionSecurity == CONN_SECURITY_TLS { if config.EmailSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
if err = c.Auth(auth); err != nil { if err = c.Auth(auth); err != nil {
return nil, model.NewAppError("SendMail", "Failed to authenticate on SMTP server", err.Error()) return nil, model.NewAppError("SendMail", "Failed to authenticate on SMTP server", err.Error())
} }
} else if config.EmailSettings.ConnectionSecurity == CONN_SECURITY_TLS { } else if config.EmailSettings.ConnectionSecurity == model.CONN_SECURITY_STARTTLS {
tlsconfig := &tls.Config{ tlsconfig := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
ServerName: config.EmailSettings.SMTPServer, ServerName: config.EmailSettings.SMTPServer,

Просмотреть файл

@@ -9,6 +9,7 @@ var LoadingScreen = require('../loading_screen.jsx');
var EmailSettingsTab = require('./email_settings.jsx'); var EmailSettingsTab = require('./email_settings.jsx');
var LogSettingsTab = require('./log_settings.jsx'); var LogSettingsTab = require('./log_settings.jsx');
var LogsTab = require('./logs.jsx'); var LogsTab = require('./logs.jsx');
var ImageSettingsTab = require('./image_settings.jsx');
export default class AdminController extends React.Component { export default class AdminController extends React.Component {
constructor(props) { constructor(props) {
@@ -53,6 +54,8 @@ export default class AdminController extends React.Component {
tab = <LogSettingsTab config={this.state.config} />; tab = <LogSettingsTab config={this.state.config} />;
} else if (this.state.selected === 'logs') { } else if (this.state.selected === 'logs') {
tab = <LogsTab />; tab = <LogsTab />;
} else if (this.state.selected === 'image_settings') {
tab = <ImageSettingsTab config={this.state.config} />;
} }
} }

Просмотреть файл

@@ -65,6 +65,15 @@ export default class AdminSidebar extends React.Component {
{'Logs'} {'Logs'}
</a> </a>
</li> </li>
<li>
<a
href='#'
className={this.isSelected('image_settings')}
onClick={this.handleClick.bind(this, 'image_settings')}
>
{'Image Settings'}
</a>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>

Просмотреть файл

@@ -0,0 +1,417 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var Client = require('../../utils/client.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
export default class ImageSettings extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
saveNeeded: false,
serverError: null,
DriverName: this.props.config.ImageSettings.DriverName
};
}
handleChange(action) {
var s = {saveNeeded: true, serverError: this.state.serverError};
if (action === 'DriverName') {
s.DriverName = React.findDOMNode(this.refs.DriverName).value;
}
this.setState(s);
}
handleSubmit(e) {
e.preventDefault();
$('#save-button').button('loading');
var config = this.props.config;
config.ImageSettings.DriverName = React.findDOMNode(this.refs.DriverName).value;
config.ImageSettings.Directory = React.findDOMNode(this.refs.Directory).value;
config.ImageSettings.AmazonS3AccessKeyId = React.findDOMNode(this.refs.AmazonS3AccessKeyId).value;
config.ImageSettings.AmazonS3SecretAccessKey = React.findDOMNode(this.refs.AmazonS3SecretAccessKey).value;
config.ImageSettings.AmazonS3Bucket = React.findDOMNode(this.refs.AmazonS3Bucket).value;
config.ImageSettings.AmazonS3Region = React.findDOMNode(this.refs.AmazonS3Region).value;
var thumbnailWidth = 120;
if (!isNaN(parseInt(React.findDOMNode(this.refs.ThumbnailWidth).value, 10))) {
thumbnailWidth = parseInt(React.findDOMNode(this.refs.ThumbnailWidth).value, 10);
}
config.ImageSettings.ThumbnailWidth = thumbnailWidth;
React.findDOMNode(this.refs.ThumbnailWidth).value = thumbnailWidth;
var thumbnailHeight = 100;
if (!isNaN(parseInt(React.findDOMNode(this.refs.ThumbnailHeight).value, 10))) {
thumbnailHeight = parseInt(React.findDOMNode(this.refs.ThumbnailHeight).value, 10);
}
config.ImageSettings.ThumbnailHeight = thumbnailHeight;
React.findDOMNode(this.refs.ThumbnailHeight).value = thumbnailHeight;
var previewWidth = 1024;
if (!isNaN(parseInt(React.findDOMNode(this.refs.PreviewWidth).value, 10))) {
previewWidth = parseInt(React.findDOMNode(this.refs.PreviewWidth).value, 10);
}
config.ImageSettings.PreviewWidth = previewWidth;
React.findDOMNode(this.refs.PreviewWidth).value = previewWidth;
var previewHeight = 0;
if (!isNaN(parseInt(React.findDOMNode(this.refs.PreviewHeight).value, 10))) {
previewHeight = parseInt(React.findDOMNode(this.refs.PreviewHeight).value, 10);
}
config.ImageSettings.PreviewHeight = previewHeight;
React.findDOMNode(this.refs.PreviewHeight).value = previewHeight;
var profileWidth = 128;
if (!isNaN(parseInt(React.findDOMNode(this.refs.ProfileWidth).value, 10))) {
profileWidth = parseInt(React.findDOMNode(this.refs.ProfileWidth).value, 10);
}
config.ImageSettings.ProfileWidth = profileWidth;
React.findDOMNode(this.refs.ProfileWidth).value = profileWidth;
var profileHeight = 128;
if (!isNaN(parseInt(React.findDOMNode(this.refs.ProfileHeight).value, 10))) {
profileHeight = parseInt(React.findDOMNode(this.refs.ProfileHeight).value, 10);
}
config.ImageSettings.ProfileHeight = profileHeight;
React.findDOMNode(this.refs.ProfileHeight).value = profileHeight;
Client.saveConfig(
config,
() => {
AsyncClient.getConfig();
this.setState({
serverError: null,
saveNeeded: false
});
$('#save-button').button('reset');
},
(err) => {
this.setState({
serverError: err.message,
saveNeeded: true
});
$('#save-button').button('reset');
}
);
}
render() {
var serverError = '';
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
}
var saveClass = 'btn';
if (this.state.saveNeeded) {
saveClass = 'btn btn-primary';
}
var enableFile = false;
var enableS3 = false;
if (this.state.DriverName === 'local') {
enableFile = true;
}
if (this.state.DriverName === 'amazons3') {
enableS3 = true;
}
return (
<div className='wrapper--fixed'>
<h3>{'Image Settings'}</h3>
<form
className='form-horizontal'
role='form'
>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='DriverName'
>
{'Store Files In:'}
</label>
<div className='col-sm-8'>
<select
className='form-control'
id='DriverName'
ref='DriverName'
defaultValue={this.props.config.ImageSettings.DriverName}
onChange={this.handleChange.bind(this, 'DriverName')}
>
<option value=''>{'Disable File Storage'}</option>
<option value='local'>{'Local File System'}</option>
<option value='amazons3'>{'Amazon S3'}</option>
</select>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='Directory'
>
{'Local Directory Location:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='Directory'
ref='Directory'
placeholder='Ex "./data/"'
defaultValue={this.props.config.ImageSettings.Directory}
onChange={this.handleChange}
disabled={!enableFile}
/>
<p className='help-text'>{'Directory to which image files are written. If blank, will be set to ./data/.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='AmazonS3AccessKeyId'
>
{'Amazon S3 Access Key Id:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='AmazonS3AccessKeyId'
ref='AmazonS3AccessKeyId'
placeholder='Ex "AKIADTOVBGERKLCBV"'
defaultValue={this.props.config.ImageSettings.AmazonS3AccessKeyId}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'Obtain this credential from your Amazon EC2 administrator.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='AmazonS3SecretAccessKey'
>
{'Amazon S3 Secret Access Key:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='AmazonS3SecretAccessKey'
ref='AmazonS3SecretAccessKey'
placeholder='Ex "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY"'
defaultValue={this.props.config.ImageSettings.AmazonS3SecretAccessKey}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'Obtain this credential from your Amazon EC2 administrator.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='AmazonS3Bucket'
>
{'Amazon S3 Bucket:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='AmazonS3Bucket'
ref='AmazonS3Bucket'
placeholder='Ex "mattermost-media"'
defaultValue={this.props.config.ImageSettings.AmazonS3Bucket}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'Name you selected for your S3 bucket in AWS.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='AmazonS3Region'
>
{'Amazon S3 Region:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='AmazonS3Region'
ref='AmazonS3Region'
placeholder='Ex "us-east-1"'
defaultValue={this.props.config.ImageSettings.AmazonS3Region}
onChange={this.handleChange}
disabled={!enableS3}
/>
<p className='help-text'>{'AWS region you selected for creating your S3 bucket.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='ThumbnailWidth'
>
{'Thumbnail Width:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='ThumbnailWidth'
ref='ThumbnailWidth'
placeholder='Ex "120"'
defaultValue={this.props.config.ImageSettings.ThumbnailWidth}
onChange={this.handleChange}
/>
<p className='help-text'>{'Width of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='ThumbnailHeight'
>
{'Thumbnail Height:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='ThumbnailHeight'
ref='ThumbnailHeight'
placeholder='Ex "100"'
defaultValue={this.props.config.ImageSettings.ThumbnailHeight}
onChange={this.handleChange}
/>
<p className='help-text'>{'Height of thumbnails generated from uploaded images. Updating this value changes how thumbnail images render in future, but does not change images created in the past.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='PreviewWidth'
>
{'Preview Width:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='PreviewWidth'
ref='PreviewWidth'
placeholder='Ex "1024"'
defaultValue={this.props.config.ImageSettings.PreviewWidth}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum width of preview image. Updating this value changes how preview images render in future, but does not change images created in the past.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='PreviewHeight'
>
{'Preview Height:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='PreviewHeight'
ref='PreviewHeight'
placeholder='Ex "0"'
defaultValue={this.props.config.ImageSettings.PreviewHeight}
onChange={this.handleChange}
/>
<p className='help-text'>{'Maximum height of preview image ("0": Sets to auto-size). Updating this value changes how preview images render in future, but does not change images created in the past.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='ProfileWidth'
>
{'Profile Width:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='ProfileWidth'
ref='ProfileWidth'
placeholder='Ex "1024"'
defaultValue={this.props.config.ImageSettings.ProfileWidth}
onChange={this.handleChange}
/>
<p className='help-text'>{'Width of profile picture.'}</p>
</div>
</div>
<div className='form-group'>
<label
className='control-label col-sm-4'
htmlFor='ProfileHeight'
>
{'Profile Height:'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
id='ProfileHeight'
ref='ProfileHeight'
placeholder='Ex "0"'
defaultValue={this.props.config.ImageSettings.ProfileHeight}
onChange={this.handleChange}
/>
<p className='help-text'>{'Height of profile picture.'}</p>
</div>
</div>
<div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
disabled={!this.state.saveNeeded}
type='submit'
className={saveClass}
onClick={this.handleSubmit}
id='save-button'
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
>
{'Save'}
</button>
</div>
</div>
</form>
</div>
);
}
}
ImageSettings.propTypes = {
config: React.PropTypes.object
};