mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
Embrace the weekend- Update to rc.3 | Read the HISTORY.md
This commit is contained in:
parent
4a446ac1e2
commit
f83b532835
26
HISTORY.md
26
HISTORY.md
|
@ -1,5 +1,31 @@
|
|||
# History
|
||||
|
||||
## 3.0.0-rc.2 -> 3.0.0-rc.3
|
||||
|
||||
Breaking changes:
|
||||
- Move middleware & their configs to the [iris-contrib/middleware](https://github.com/iris-contrib/middleware) repository
|
||||
- Move all plugins & their configs to the [iris-contrib/plugin](https://github.com/iris-contrib/plugin) repository
|
||||
- Move the graceful package to the [iris-contrib/graceful](https://github.com/iris-contrib/graceful) repository
|
||||
- Move the mail package & its configs to the [iris-contrib/mail](https://github.com/iris-contrib/mail) repository
|
||||
|
||||
Note 1: iris.Config.Mail doesn't not logger exists, use ` mail.Config` from the `iris-contrib/mail`, and ` service:= mail.New(configs); service.Send(....)`.
|
||||
|
||||
Note 2: basicauth middleware's context key changed from `context.GetString("auth")` to ` context.GetString("user")`.
|
||||
|
||||
Underline changes, libraries used by iris' base code:
|
||||
- Move the errors package to the [iris-contrib/errors](https://github.com/iris-contrib/errors) repository
|
||||
- Move the tests package to the [iris-contrib/tests](https://github.com/iris-contrib/tests) repository (Yes, you should make PRs now with no fear about breaking the Iris).
|
||||
|
||||
NEW:
|
||||
- OAuth, OAuth2 support via plugin (facebook,gplus,twitter and 25 more), gitbook section [here](https://kataras.gitbooks.io/iris/content/plugin-oauth.html), plugin [example](https://github.com/iris-contrib/examples/blob/master/plugin_oauth_oauth2/main.go), low-level package example [here](https://github.com/iris-contrib/examples/tree/master/oauth_oauth2) (no performance differences, it's just a working version of [goth](https://github.com/markbates/goth) which is converted to work with Iris).
|
||||
|
||||
Fixes:
|
||||
- [Iris run fails when not running from ./](https://github.com/kataras/iris/issues/215)
|
||||
- [Fix or disable colors in iris run](https://github.com/kataras/iris/issues/217).
|
||||
|
||||
|
||||
[Book](https://kataras.gitbooks.io/iris/content/) and [examples](https://github.com/iris-contrib/examples) are updated also.
|
||||
|
||||
## 3.0.0-rc.1 -> 3.0.0-rc.2
|
||||
|
||||
New:
|
||||
|
|
26
README.md
26
README.md
|
@ -6,7 +6,7 @@
|
|||
[Travis]: http://travis-ci.org/kataras/iris
|
||||
[License Widget]: https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square
|
||||
[License]: https://github.com/kataras/iris/blob/master/LICENSE
|
||||
[Release Widget]: https://img.shields.io/badge/release-v3.0.0--rc.2-blue.svg?style=flat-square
|
||||
[Release Widget]: https://img.shields.io/badge/release-v3.0.0--rc.3-blue.svg?style=flat-square
|
||||
[Release]: https://github.com/kataras/iris/releases
|
||||
[Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square
|
||||
[Gitter]: https://gitter.im/kataras/iris
|
||||
|
@ -138,19 +138,35 @@ You can find answers by exploring [these questions](https://github.com/kataras/i
|
|||
Features
|
||||
------------
|
||||
- Focus on high performance
|
||||
- Robust routing & static, wildcard subdomains
|
||||
- Robust routing supports static and wildcard subdomains
|
||||
- View system supporting [5+](https://kataras.gitbooks.io/iris/content/render_templates.html) template engines
|
||||
- Highly scalable Websocket API with custom events
|
||||
- Sessions support with GC, memory & redis providers
|
||||
- Middlewares & Plugins were never be easier
|
||||
- Full REST API
|
||||
- Custom HTTP Errors
|
||||
- Typescript compiler + Browser editor
|
||||
- Typescript compiler + Browser-based editor
|
||||
- Content negotiation & streaming
|
||||
- Transport Layer Security
|
||||
- [Reload](https://github.com/kataras/iris/tree/master/iris#run) on source code changes
|
||||
- and much more
|
||||
- OAuth, OAuth2 provider supporting 27+ API providers
|
||||
- and more
|
||||
|
||||
<img src="https://raw.githubusercontent.com/iris-contrib/website/gh-pages/assets/arrowdown.png" width="72"/>
|
||||
|
||||
|
||||
| Name | Description | Usage |
|
||||
| ------------------|:---------------------:|-------:|
|
||||
| [Basicauth Middleware ](https://github.com/iris-contrib/middleware/tree/master/basicauth) | HTTP Basic authentication |[example 1](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_1/main.go), [example 2](https://github.com/iris-contrib/examples/blob/master/middleware_basicauth_2/main.go), [book section](https://kataras.gitbooks.io/iris/content/basic-authentication.html) |
|
||||
| [Cors Middleware ](https://github.com/iris-contrib/middleware/tree/master/cors) | Cross Origin Resource Sharing W3 specification | [how to use ](https://github.com/iris-contrib/middleware/tree/master/cors#how-to-use) |
|
||||
| [Secure Middleware ](https://github.com/iris-contrib/middleware/tree/master/secure) | Facilitates some quick security wins | [example](https://github.com/iris-contrib/examples/blob/master/middleware_secure/main.go) |
|
||||
| [I18n Middleware ](https://github.com/iris-contrib/middleware/tree/master/i18n) | Simple internationalization | [example](https://github.com/iris-contrib/examples/tree/master/middleware_internationalization_i18n), [book section](https://kataras.gitbooks.io/iris/content/middleware-internationalization-and-localization.html) |
|
||||
| [Recovery Middleware ](https://github.com/iris-contrib/middleware/tree/master/recovery) | Safety recover the station from panic | [example](https://github.com/iris-contrib/examples/blob/master/middleware_recovery/main.go) |
|
||||
| [Logger Middleware ](https://github.com/iris-contrib/middleware/tree/master/logger) | Logs every request | [example](https://github.com/iris-contrib/examples/blob/master/middleware_logger/main.go), [book section](https://kataras.gitbooks.io/iris/content/logger.html) |
|
||||
| [Editor Plugin](https://github.com/iris-contrib/plugin/tree/master/editor) | Alm-tools, a typescript online IDE/Editor | [book section](https://kataras.gitbooks.io/iris/content/plugin-editor.html) |
|
||||
| [Typescript Plugin](https://github.com/iris-contrib/plugin/tree/master/typescript) | Auto-compile client-side typescript files | [book section](https://kataras.gitbooks.io/iris/content/plugin-typescript.html) |
|
||||
| [OAuth,OAuth2 Plugin](https://github.com/iris-contrib/plugin/tree/master/oauth) | User Authentication was never be easier, supports >27 providers | [example](https://github.com/iris-contrib/examples/tree/master/plugin_oauth_oauth2), [book section](https://kataras.gitbooks.io/iris/content/plugin-oauth.html) |
|
||||
| [Iris control Plugin](https://github.com/iris-contrib/plugin/tree/master/iriscontrol) | Basic (browser-based) control over your Iris station | [example](https://github.com/iris-contrib/examples/blob/master/plugin_iriscontrol/main.go), [book section](https://kataras.gitbooks.io/iris/content/plugin-iriscontrol.html) |
|
||||
|
||||
Docs & Community
|
||||
------------
|
||||
|
@ -202,7 +218,7 @@ Iris suggests you to use [this](https://github.com/gavv/httpexpect) new suite t
|
|||
Versioning
|
||||
------------
|
||||
|
||||
Current: **v3.0.0-rc.2**
|
||||
Current: **v3.0.0-rc.3**
|
||||
> Iris is an active project
|
||||
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultBasicAuthRealm is "Authorization Required"
|
||||
DefaultBasicAuthRealm = "Authorization Required"
|
||||
// DefaultBasicAuthContextKey is the "auth"
|
||||
// this key is used to do context.Set("auth", theUsernameFromBasicAuth)
|
||||
DefaultBasicAuthContextKey = "auth"
|
||||
)
|
||||
|
||||
// BasicAuth the configs for the basicauth middleware
|
||||
type BasicAuth struct {
|
||||
// Users a map of login and the value (username/password)
|
||||
Users map[string]string
|
||||
// Realm http://tools.ietf.org/html/rfc2617#section-1.2. Default is "Authorization Required"
|
||||
Realm string
|
||||
// ContextKey the key for ctx.GetString(...). Default is 'auth'
|
||||
ContextKey string
|
||||
// Expires expiration duration, default is 0 never expires
|
||||
Expires time.Duration
|
||||
}
|
||||
|
||||
// DefaultBasicAuth returns the default configs for the BasicAuth middleware
|
||||
func DefaultBasicAuth() BasicAuth {
|
||||
return BasicAuth{make(map[string]string), DefaultBasicAuthRealm, DefaultBasicAuthContextKey, 0}
|
||||
}
|
||||
|
||||
// MergeSingle merges the default with the given config and returns the result
|
||||
func (c BasicAuth) MergeSingle(cfg BasicAuth) (config BasicAuth) {
|
||||
|
||||
config = cfg
|
||||
mergo.Merge(&config, c)
|
||||
|
||||
return
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package config
|
||||
|
||||
import "github.com/imdario/mergo"
|
||||
|
||||
// Editor the configs for the Editor plugin
|
||||
type Editor struct {
|
||||
// Host if empty used the iris server's host
|
||||
Host string
|
||||
// Port if 0 4444
|
||||
Port int
|
||||
// WorkingDir if empty "./"
|
||||
WorkingDir string
|
||||
// Username if empty iris
|
||||
Username string
|
||||
// Password if empty admin!123
|
||||
Password string
|
||||
}
|
||||
|
||||
// DefaultEditor returns the default configs for the Editor plugin
|
||||
func DefaultEditor() Editor {
|
||||
return Editor{"", 4444, "." + pathSeparator, DefaultUsername, DefaultPassword}
|
||||
}
|
||||
|
||||
// Merge merges the default with the given config and returns the result
|
||||
func (c Editor) Merge(cfg []Editor) (config Editor) {
|
||||
|
||||
if len(cfg) > 0 {
|
||||
config = cfg[0]
|
||||
mergo.Merge(&config, c)
|
||||
} else {
|
||||
_default := c
|
||||
config = _default
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MergeSingle merges the default with the given config and returns the result
|
||||
func (c Editor) MergeSingle(cfg Editor) (config Editor) {
|
||||
|
||||
config = cfg
|
||||
mergo.Merge(&config, c)
|
||||
|
||||
return
|
||||
}
|
|
@ -101,13 +101,6 @@ type (
|
|||
// Websocket contains the configs for Websocket's server integration
|
||||
Websocket *Websocket
|
||||
|
||||
// Mail contains the configs for the mail sender service
|
||||
Mail Mail
|
||||
|
||||
// OAuth the configs for the gothic oauth/oauth2 authentication for third-party websites
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/example/main.go
|
||||
OAuth OAuth
|
||||
|
||||
// Server contains the configs for the http server
|
||||
// Server configs are the only one which are setted inside base Iris package (from Listen, ListenTLS, ListenUNIX) NO from users
|
||||
//
|
||||
|
@ -150,8 +143,6 @@ func Default() Iris {
|
|||
Sessions: DefaultSessions(),
|
||||
Render: DefaultRender(),
|
||||
Websocket: DefaultWebsocket(),
|
||||
Mail: DefaultMail(),
|
||||
OAuth: DefaultOAuth(),
|
||||
Server: DefaultServer(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package config
|
||||
|
||||
import "github.com/imdario/mergo"
|
||||
|
||||
var (
|
||||
// DefaultUsername used for default (basic auth) username in IrisControl's & Editor's default configuration
|
||||
DefaultUsername = "iris"
|
||||
// DefaultPassword used for default (basic auth) password in IrisControl's & Editor's default configuration
|
||||
DefaultPassword = "admin!123"
|
||||
)
|
||||
|
||||
// IrisControl the options which iris control needs
|
||||
// contains the port (int) and authenticated users with their passwords (map[string]string)
|
||||
type IrisControl struct {
|
||||
// Port the port
|
||||
Port int
|
||||
// Users the authenticated users, [username]password
|
||||
Users map[string]string
|
||||
}
|
||||
|
||||
// DefaultIrisControl returns the default configs for IrisControl plugin
|
||||
func DefaultIrisControl() IrisControl {
|
||||
users := make(map[string]string, 0)
|
||||
users[DefaultUsername] = DefaultPassword
|
||||
return IrisControl{4000, users}
|
||||
}
|
||||
|
||||
// Merge merges the default with the given config and returns the result
|
||||
func (c IrisControl) Merge(cfg []IrisControl) (config IrisControl) {
|
||||
|
||||
if len(cfg) > 0 {
|
||||
config = cfg[0]
|
||||
mergo.Merge(&config, c)
|
||||
} else {
|
||||
_default := c
|
||||
config = _default
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -80,12 +80,12 @@ func DefaultLogger() Logger {
|
|||
ColorFgDanger: int(color.FgHiRed),
|
||||
ColorFgOther: int(color.FgHiYellow),
|
||||
// background colors
|
||||
ColorBgDefault: int(color.BgHiBlack),
|
||||
ColorBgInfo: int(color.BgHiBlack),
|
||||
ColorBgSuccess: int(color.BgHiBlack),
|
||||
ColorBgWarning: int(color.BgHiBlack),
|
||||
ColorBgDanger: int(color.BgHiWhite),
|
||||
ColorBgOther: int(color.BgHiBlack),
|
||||
ColorBgDefault: 0,
|
||||
ColorBgInfo: 0,
|
||||
ColorBgSuccess: 0,
|
||||
ColorBgWarning: 0,
|
||||
ColorBgDanger: 0,
|
||||
ColorBgOther: 0,
|
||||
// banner color
|
||||
ColorFgBanner: int(color.FgHiBlue),
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package config
|
||||
|
||||
// Mail keeps the configs for mail sender service
|
||||
type Mail struct {
|
||||
// Host is the server mail host, IP or address
|
||||
Host string
|
||||
// Port is the listening port
|
||||
Port int
|
||||
// Username is the auth username@domain.com for the sender
|
||||
Username string
|
||||
// Password is the auth password for the sender
|
||||
Password string
|
||||
// FromAlias is the from part, if empty this is the first part before @ from the Username field
|
||||
FromAlias string
|
||||
// UseCommand enable it if you want to send e-mail with the mail command instead of smtp
|
||||
//
|
||||
// Host,Port & Password will be ignored
|
||||
// ONLY FOR UNIX
|
||||
UseCommand bool
|
||||
}
|
||||
|
||||
// DefaultMail returns the default configs for Mail
|
||||
// returns just an empty Mail struct
|
||||
func DefaultMail() Mail {
|
||||
return Mail{}
|
||||
}
|
||||
|
||||
// IsValid returns true if the mail configs are valid
|
||||
func (m Mail) IsValid() bool {
|
||||
return (m.Host != "" && m.Port > 0 && m.Username != "" && m.Password != "") || m.UseCommand
|
||||
}
|
213
config/oauth.go
213
config/oauth.go
|
@ -1,213 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/amazon"
|
||||
"github.com/markbates/goth/providers/bitbucket"
|
||||
"github.com/markbates/goth/providers/box"
|
||||
"github.com/markbates/goth/providers/digitalocean"
|
||||
"github.com/markbates/goth/providers/dropbox"
|
||||
"github.com/markbates/goth/providers/facebook"
|
||||
"github.com/markbates/goth/providers/github"
|
||||
"github.com/markbates/goth/providers/gitlab"
|
||||
"github.com/markbates/goth/providers/gplus"
|
||||
"github.com/markbates/goth/providers/heroku"
|
||||
"github.com/markbates/goth/providers/instagram"
|
||||
"github.com/markbates/goth/providers/lastfm"
|
||||
"github.com/markbates/goth/providers/linkedin"
|
||||
"github.com/markbates/goth/providers/onedrive"
|
||||
"github.com/markbates/goth/providers/paypal"
|
||||
"github.com/markbates/goth/providers/salesforce"
|
||||
"github.com/markbates/goth/providers/slack"
|
||||
"github.com/markbates/goth/providers/soundcloud"
|
||||
"github.com/markbates/goth/providers/spotify"
|
||||
"github.com/markbates/goth/providers/steam"
|
||||
"github.com/markbates/goth/providers/stripe"
|
||||
"github.com/markbates/goth/providers/twitch"
|
||||
"github.com/markbates/goth/providers/twitter"
|
||||
"github.com/markbates/goth/providers/uber"
|
||||
"github.com/markbates/goth/providers/wepay"
|
||||
"github.com/markbates/goth/providers/yahoo"
|
||||
"github.com/markbates/goth/providers/yammer"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultAuthPath /auth
|
||||
DefaultAuthPath = "/auth"
|
||||
)
|
||||
|
||||
// OAuth the configs for the gothic oauth/oauth2 authentication for third-party websites
|
||||
// All Key and Secret values are empty by default strings. Non-empty will be registered as Goth Provider automatically, by Iris
|
||||
// the users can still register their own providers using goth.UseProviders
|
||||
// contains the providers' keys (& secrets) and the relative auth callback url path(ex: "/auth" will be registered as /auth/:provider/callback)
|
||||
//
|
||||
type OAuth struct {
|
||||
Path string
|
||||
TwitterKey, TwitterSecret, TwitterName string
|
||||
FacebookKey, FacebookSecret, FacebookName string
|
||||
GplusKey, GplusSecret, GplusName string
|
||||
GithubKey, GithubSecret, GithubName string
|
||||
SpotifyKey, SpotifySecret, SpotifyName string
|
||||
LinkedinKey, LinkedinSecret, LinkedinName string
|
||||
LastfmKey, LastfmSecret, LastfmName string
|
||||
TwitchKey, TwitchSecret, TwitchName string
|
||||
DropboxKey, DropboxSecret, DropboxName string
|
||||
DigitaloceanKey, DigitaloceanSecret, DigitaloceanName string
|
||||
BitbucketKey, BitbucketSecret, BitbucketName string
|
||||
InstagramKey, InstagramSecret, InstagramName string
|
||||
BoxKey, BoxSecret, BoxName string
|
||||
SalesforceKey, SalesforceSecret, SalesforceName string
|
||||
AmazonKey, AmazonSecret, AmazonName string
|
||||
YammerKey, YammerSecret, YammerName string
|
||||
OneDriveKey, OneDriveSecret, OneDriveName string
|
||||
YahooKey, YahooSecret, YahooName string
|
||||
SlackKey, SlackSecret, SlackName string
|
||||
StripeKey, StripeSecret, StripeName string
|
||||
WepayKey, WepaySecret, WepayName string
|
||||
PaypalKey, PaypalSecret, PaypalName string
|
||||
SteamKey, SteamName string
|
||||
HerokuKey, HerokuSecret, HerokuName string
|
||||
UberKey, UberSecret, UberName string
|
||||
SoundcloudKey, SoundcloudSecret, SoundcloudName string
|
||||
GitlabKey, GitlabSecret, GitlabName string
|
||||
}
|
||||
|
||||
// DefaultOAuth returns OAuth config, the fields of the iteral are zero-values ( empty strings)
|
||||
func DefaultOAuth() OAuth {
|
||||
return OAuth{
|
||||
Path: DefaultAuthPath,
|
||||
TwitterName: "twitter",
|
||||
FacebookName: "facebook",
|
||||
GplusName: "gplus",
|
||||
GithubName: "github",
|
||||
SpotifyName: "spotify",
|
||||
LinkedinName: "linkedin",
|
||||
LastfmName: "lastfm",
|
||||
TwitchName: "twitch",
|
||||
DropboxName: "dropbox",
|
||||
DigitaloceanName: "digitalocean",
|
||||
BitbucketName: "bitbucket",
|
||||
InstagramName: "instagram",
|
||||
BoxName: "box",
|
||||
SalesforceName: "salesforce",
|
||||
AmazonName: "amazon",
|
||||
YammerName: "yammer",
|
||||
OneDriveName: "onedrive",
|
||||
YahooName: "yahoo",
|
||||
SlackName: "slack",
|
||||
StripeName: "stripe",
|
||||
WepayName: "wepay",
|
||||
PaypalName: "paypal",
|
||||
SteamName: "steam",
|
||||
HerokuName: "heroku",
|
||||
UberName: "uber",
|
||||
SoundcloudName: "soundcloud",
|
||||
GitlabName: "gitlab",
|
||||
} // this will be registered as /auth/:provider in the mux
|
||||
}
|
||||
|
||||
// MergeSingle merges the default with the given config and returns the result
|
||||
func (c OAuth) MergeSingle(cfg OAuth) (config OAuth) {
|
||||
|
||||
config = cfg
|
||||
mergo.Merge(&config, c)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAll returns the valid goth providers and the relative url paths (because the goth.Provider doesn't have a public method to get the Auth path...)
|
||||
// we do the hard-core/hand checking here at the configs.
|
||||
//
|
||||
// receives one parameter which is the host from the server,ex: http://localhost:3000, will be used as prefix for the oauth callback
|
||||
func (c OAuth) GetAll(vhost string) (providers []goth.Provider) {
|
||||
|
||||
getCallbackURL := func(providerName string) string {
|
||||
return vhost + c.Path + "/" + providerName + "/callback"
|
||||
}
|
||||
|
||||
//we could use a map but that's easier for the users because of code completion of their IDEs/editors
|
||||
if c.TwitterKey != "" && c.TwitterSecret != "" {
|
||||
println(getCallbackURL("twitter"))
|
||||
providers = append(providers, twitter.New(c.TwitterKey, c.TwitterSecret, getCallbackURL(c.TwitterName)))
|
||||
}
|
||||
if c.FacebookKey != "" && c.FacebookSecret != "" {
|
||||
providers = append(providers, facebook.New(c.FacebookKey, c.FacebookSecret, getCallbackURL(c.FacebookName)))
|
||||
}
|
||||
if c.GplusKey != "" && c.GplusSecret != "" {
|
||||
providers = append(providers, gplus.New(c.GplusKey, c.GplusSecret, getCallbackURL(c.GplusName)))
|
||||
}
|
||||
if c.GithubKey != "" && c.GithubSecret != "" {
|
||||
providers = append(providers, github.New(c.GithubKey, c.GithubSecret, getCallbackURL(c.GithubName)))
|
||||
}
|
||||
if c.SpotifyKey != "" && c.SpotifySecret != "" {
|
||||
providers = append(providers, spotify.New(c.SpotifyKey, c.SpotifySecret, getCallbackURL(c.SpotifyName)))
|
||||
}
|
||||
if c.LinkedinKey != "" && c.LinkedinSecret != "" {
|
||||
providers = append(providers, linkedin.New(c.LinkedinKey, c.LinkedinSecret, getCallbackURL(c.LinkedinName)))
|
||||
}
|
||||
if c.LastfmKey != "" && c.LastfmSecret != "" {
|
||||
providers = append(providers, lastfm.New(c.LastfmKey, c.LastfmSecret, getCallbackURL(c.LastfmName)))
|
||||
}
|
||||
if c.TwitchKey != "" && c.TwitchSecret != "" {
|
||||
providers = append(providers, twitch.New(c.TwitchKey, c.TwitchSecret, getCallbackURL(c.TwitchName)))
|
||||
}
|
||||
if c.DropboxKey != "" && c.DropboxSecret != "" {
|
||||
providers = append(providers, dropbox.New(c.DropboxKey, c.DropboxSecret, getCallbackURL(c.DropboxName)))
|
||||
}
|
||||
if c.DigitaloceanKey != "" && c.DigitaloceanSecret != "" {
|
||||
providers = append(providers, digitalocean.New(c.DigitaloceanKey, c.DigitaloceanSecret, getCallbackURL(c.DigitaloceanName)))
|
||||
}
|
||||
if c.BitbucketKey != "" && c.BitbucketSecret != "" {
|
||||
providers = append(providers, bitbucket.New(c.BitbucketKey, c.BitbucketSecret, getCallbackURL(c.BitbucketName)))
|
||||
}
|
||||
if c.InstagramKey != "" && c.InstagramSecret != "" {
|
||||
providers = append(providers, instagram.New(c.InstagramKey, c.InstagramSecret, getCallbackURL(c.InstagramName)))
|
||||
}
|
||||
if c.BoxKey != "" && c.BoxSecret != "" {
|
||||
providers = append(providers, box.New(c.BoxKey, c.BoxSecret, getCallbackURL(c.BoxName)))
|
||||
}
|
||||
if c.SalesforceKey != "" && c.SalesforceSecret != "" {
|
||||
providers = append(providers, salesforce.New(c.SalesforceKey, c.SalesforceSecret, getCallbackURL(c.SalesforceName)))
|
||||
}
|
||||
if c.AmazonKey != "" && c.AmazonSecret != "" {
|
||||
providers = append(providers, amazon.New(c.AmazonKey, c.AmazonSecret, getCallbackURL(c.AmazonName)))
|
||||
}
|
||||
if c.YammerKey != "" && c.YammerSecret != "" {
|
||||
providers = append(providers, yammer.New(c.YammerKey, c.YammerSecret, getCallbackURL(c.YammerName)))
|
||||
}
|
||||
if c.OneDriveKey != "" && c.OneDriveSecret != "" {
|
||||
providers = append(providers, onedrive.New(c.OneDriveKey, c.OneDriveSecret, getCallbackURL(c.OneDriveName)))
|
||||
}
|
||||
if c.YahooKey != "" && c.YahooSecret != "" {
|
||||
providers = append(providers, yahoo.New(c.YahooKey, c.YahooSecret, getCallbackURL(c.YahooName)))
|
||||
}
|
||||
if c.SlackKey != "" && c.SlackSecret != "" {
|
||||
providers = append(providers, slack.New(c.SlackKey, c.SlackSecret, getCallbackURL(c.SlackName)))
|
||||
}
|
||||
if c.StripeKey != "" && c.StripeSecret != "" {
|
||||
providers = append(providers, stripe.New(c.StripeKey, c.StripeSecret, getCallbackURL(c.StripeName)))
|
||||
}
|
||||
if c.WepayKey != "" && c.WepaySecret != "" {
|
||||
providers = append(providers, wepay.New(c.WepayKey, c.WepaySecret, getCallbackURL(c.WepayName)))
|
||||
}
|
||||
if c.PaypalKey != "" && c.PaypalSecret != "" {
|
||||
providers = append(providers, paypal.New(c.PaypalKey, c.PaypalSecret, getCallbackURL(c.PaypalName)))
|
||||
}
|
||||
if c.SteamKey != "" {
|
||||
providers = append(providers, steam.New(c.SteamKey, getCallbackURL(c.SteamName)))
|
||||
}
|
||||
if c.HerokuKey != "" && c.HerokuSecret != "" {
|
||||
providers = append(providers, heroku.New(c.HerokuKey, c.HerokuSecret, getCallbackURL(c.HerokuName)))
|
||||
}
|
||||
if c.UberKey != "" && c.UberSecret != "" {
|
||||
providers = append(providers, uber.New(c.UberKey, c.UberSecret, getCallbackURL(c.UberName)))
|
||||
}
|
||||
if c.SoundcloudKey != "" && c.SoundcloudSecret != "" {
|
||||
providers = append(providers, soundcloud.New(c.SoundcloudKey, c.SoundcloudSecret, getCallbackURL(c.SoundcloudName)))
|
||||
}
|
||||
if c.GitlabKey != "" && c.GitlabSecret != "" {
|
||||
providers = append(providers, gitlab.New(c.GitlabKey, c.GitlabSecret, getCallbackURL(c.GithubName)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
)
|
||||
|
||||
var (
|
||||
pathSeparator = string(os.PathSeparator)
|
||||
nodeModules = pathSeparator + "node_modules" + pathSeparator
|
||||
)
|
||||
|
||||
type (
|
||||
// Tsconfig the struct for tsconfig.json
|
||||
Tsconfig struct {
|
||||
CompilerOptions CompilerOptions `json:"compilerOptions"`
|
||||
Exclude []string `json:"exclude"`
|
||||
}
|
||||
|
||||
// CompilerOptions contains all the compiler options used by the tsc (typescript compiler)
|
||||
CompilerOptions struct {
|
||||
Declaration bool `json:"declaration"`
|
||||
Module string `json:"module"`
|
||||
Target string `json:"target"`
|
||||
Watch bool `json:"watch"`
|
||||
Charset string `json:"charset"`
|
||||
Diagnostics bool `json:"diagnostics"`
|
||||
EmitBOM bool `json:"emitBOM"`
|
||||
EmitDecoratorMetadata bool `json:"emitDecoratorMetadata"`
|
||||
ExperimentalDecorators bool `json:"experimentalDecorators"`
|
||||
InlineSourceMap bool `json:"inlineSourceMap"`
|
||||
InlineSources bool `json:"inlineSources"`
|
||||
IsolatedModules bool `json:"isolatedModules"`
|
||||
Jsx string `json:"jsx"`
|
||||
ReactNamespace string `json:"reactNamespace"`
|
||||
ListFiles bool `json:"listFiles"`
|
||||
Locale string `json:"locale"`
|
||||
MapRoot string `json:"mapRoot"`
|
||||
ModuleResolution string `json:"moduleResolution"`
|
||||
NewLine string `json:"newLine"`
|
||||
NoEmit bool `json:"noEmit"`
|
||||
NoEmitOnError bool `json:"noEmitOnError"`
|
||||
NoEmitHelpers bool `json:"noEmitHelpers"`
|
||||
NoImplicitAny bool `json:"noImplicitAny"`
|
||||
NoLib bool `json:"noLib"`
|
||||
NoResolve bool `json:"noResolve"`
|
||||
SkipDefaultLibCheck bool `json:"skipDefaultLibCheck"`
|
||||
OutDir string `json:"outDir"`
|
||||
OutFile string `json:"outFile"`
|
||||
PreserveConstEnums bool `json:"preserveConstEnums"`
|
||||
Pretty bool `json:"pretty"`
|
||||
RemoveComments bool `json:"removeComments"`
|
||||
RootDir string `json:"rootDir"`
|
||||
SourceMap bool `json:"sourceMap"`
|
||||
SourceRoot string `json:"sourceRoot"`
|
||||
StripInternal bool `json:"stripInternal"`
|
||||
SuppressExcessPropertyErrors bool `json:"suppressExcessPropertyErrors"`
|
||||
SuppressImplicitAnyIndexErrors bool `json:"suppressImplicitAnyIndexErrors"`
|
||||
AllowUnusedLabels bool `json:"allowUnusedLabels"`
|
||||
NoImplicitReturns bool `json:"noImplicitReturns"`
|
||||
NoFallthroughCasesInSwitch bool `json:"noFallthroughCasesInSwitch"`
|
||||
AllowUnreachableCode bool `json:"allowUnreachableCode"`
|
||||
ForceConsistentCasingInFileNames bool `json:"forceConsistentCasingInFileNames"`
|
||||
AllowSyntheticDefaultImports bool `json:"allowSyntheticDefaultImports"`
|
||||
AllowJs bool `json:"allowJs"`
|
||||
NoImplicitUseStrict bool `json:"noImplicitUseStrict"`
|
||||
}
|
||||
|
||||
// Typescript the configs for the Typescript plugin
|
||||
Typescript struct {
|
||||
// Bin the path of the tsc binary file
|
||||
// if empty then the plugin tries to find it
|
||||
Bin string
|
||||
// Dir the client side directory, which typescript (.ts) files are live
|
||||
Dir string
|
||||
// Ignore ignore folders, default is /node_modules/
|
||||
Ignore string
|
||||
// Tsconfig the typescript build configs, including the compiler's options
|
||||
Tsconfig Tsconfig
|
||||
// Editor the Editor plugin
|
||||
Editor Editor
|
||||
}
|
||||
)
|
||||
|
||||
// CompilerArgs returns the CompilerOptions' contents of the Tsconfig
|
||||
// it reads the json tags, add '--' at the start of each one and returns an array of strings
|
||||
func (tsconfig Tsconfig) CompilerArgs() []string {
|
||||
//val := reflect.ValueOf(tsconfig).Elem().FieldByName("CompilerOptions") -> for tsconfig *Tsconfig
|
||||
val := reflect.ValueOf(tsconfig).FieldByName("CompilerOptions")
|
||||
compilerOpts := make([]string, val.NumField())
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
typeField := val.Type().Field(i)
|
||||
compilerOpts[i] = "--" + typeField.Tag.Get("json")
|
||||
}
|
||||
|
||||
return compilerOpts
|
||||
}
|
||||
|
||||
// DefaultTsconfig returns the default Tsconfig, with CompilerOptions module: commonjs, target: es5 and ignore the node_modules
|
||||
func DefaultTsconfig() Tsconfig {
|
||||
return Tsconfig{
|
||||
CompilerOptions: CompilerOptions{
|
||||
Module: "commonjs",
|
||||
Target: "es5",
|
||||
NoImplicitAny: false,
|
||||
SourceMap: false,
|
||||
},
|
||||
Exclude: []string{"node_modules"},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DefaultTypescript returns the default Options of the Typescript plugin
|
||||
// Bin and Editor are setting in runtime via the plugin
|
||||
func DefaultTypescript() Typescript {
|
||||
root, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic("Typescript Plugin: Cannot get the Current Working Directory !!! [os.getwd()]")
|
||||
}
|
||||
c := Typescript{Dir: root + pathSeparator, Ignore: nodeModules, Tsconfig: DefaultTsconfig()}
|
||||
return c
|
||||
|
||||
}
|
||||
|
||||
// Merge merges the default with the given config and returns the result
|
||||
func (c Typescript) Merge(cfg []Typescript) (config Typescript) {
|
||||
|
||||
if len(cfg) > 0 {
|
||||
config = cfg[0]
|
||||
mergo.Merge(&config, c)
|
||||
} else {
|
||||
_default := c
|
||||
config = _default
|
||||
}
|
||||
|
||||
return
|
||||
}
|
26
context.go
26
context.go
|
@ -23,14 +23,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/iris-contrib/formBinder"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/kataras/iris/sessions/store"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
|
@ -87,8 +86,6 @@ type (
|
|||
sessionStore store.IStore
|
||||
// pos is the position number of the Context, look .Next to understand
|
||||
pos uint8
|
||||
//gothic oauth
|
||||
oauthUser goth.User
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -774,28 +771,7 @@ func (ctx *Context) SessionDestroy() {
|
|||
|
||||
}
|
||||
|
||||
// SendMail sends a mail to recipients
|
||||
// the body can be html also
|
||||
func (ctx *Context) SendMail(subject string, body string, to ...string) error {
|
||||
return ctx.framework.SendMail(subject, body, to...)
|
||||
}
|
||||
|
||||
// Log logs to the iris defined logger
|
||||
func (ctx *Context) Log(format string, a ...interface{}) {
|
||||
ctx.framework.Logger.Printf(format, a...)
|
||||
}
|
||||
|
||||
/* Auth */
|
||||
|
||||
// SetOAuthUser sets the oauth user
|
||||
// Internal method but exported because useful for advanced use cases
|
||||
// Iris uses this method to set automatically the authenticated user.
|
||||
func (ctx *Context) SetOAuthUser(u goth.User) {
|
||||
ctx.oauthUser = u
|
||||
}
|
||||
|
||||
// OAuthUser returns the oauthenticated User
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/exampl/main.go to see this in action.
|
||||
func (ctx *Context) OAuthUser() goth.User {
|
||||
return ctx.oauthUser
|
||||
}
|
||||
|
|
|
@ -7,80 +7,13 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/kataras/iris/sessions/store"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type (
|
||||
// IContext the interface for the Context
|
||||
// IContext the interface for the iris/context
|
||||
// Used mostly inside packages which shouldn't be import ,directly, the kataras/iris.
|
||||
IContext interface {
|
||||
IContextRenderer
|
||||
IContextStorage
|
||||
IContextBinder
|
||||
IContextRequest
|
||||
IContextResponse
|
||||
IContextAuth
|
||||
SendMail(string, string, ...string) error
|
||||
Log(string, ...interface{})
|
||||
Reset(*fasthttp.RequestCtx)
|
||||
GetRequestCtx() *fasthttp.RequestCtx
|
||||
Clone() IContext
|
||||
Do()
|
||||
Next()
|
||||
StopExecution()
|
||||
IsStopped() bool
|
||||
GetHandlerName() string
|
||||
}
|
||||
|
||||
// IContextBinder is part of the IContext
|
||||
IContextBinder interface {
|
||||
ReadJSON(interface{}) error
|
||||
ReadXML(interface{}) error
|
||||
ReadForm(formObject interface{}) error
|
||||
}
|
||||
|
||||
// IContextRenderer is part of the IContext
|
||||
IContextRenderer interface {
|
||||
Write(string, ...interface{})
|
||||
HTML(int, string)
|
||||
// Data writes out the raw bytes as binary data.
|
||||
Data(status int, v []byte) error
|
||||
// RenderWithStatus builds up the response from the specified template and bindings.
|
||||
RenderWithStatus(status int, name string, binding interface{}, layout ...string) error
|
||||
// Render same as .RenderWithStatus but with status to iris.StatusOK (200)
|
||||
Render(name string, binding interface{}, layout ...string) error
|
||||
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
|
||||
MustRender(name string, binding interface{}, layout ...string)
|
||||
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||
// if any error returns empty string
|
||||
TemplateString(name string, binding interface{}, layout ...string) string
|
||||
// MarkdownString parses the (dynamic) markdown string and returns the converted html string
|
||||
MarkdownString(markdown string) string
|
||||
// Markdown parses and renders to the client a particular (dynamic) markdown string
|
||||
// accepts two parameters
|
||||
// first is the http status code
|
||||
// second is the markdown string
|
||||
Markdown(status int, markdown string)
|
||||
// JSON marshals the given interface object and writes the JSON response.
|
||||
JSON(status int, v interface{}) error
|
||||
// JSONP marshals the given interface object and writes the JSON response.
|
||||
JSONP(status int, callback string, v interface{}) error
|
||||
// Text writes out a string as plain text.
|
||||
Text(status int, v string) error
|
||||
// XML marshals the given interface object and writes the XML response.
|
||||
XML(status int, v interface{}) error
|
||||
|
||||
ExecuteTemplate(*template.Template, interface{}) error
|
||||
ServeContent(io.ReadSeeker, string, time.Time, bool) error
|
||||
ServeFile(string, bool) error
|
||||
SendFile(filename string, destinationName string) error
|
||||
Stream(func(*bufio.Writer))
|
||||
StreamWriter(cb func(writer *bufio.Writer))
|
||||
StreamReader(io.Reader, int)
|
||||
}
|
||||
|
||||
// IContextRequest is part of the IContext
|
||||
IContextRequest interface {
|
||||
Param(string) string
|
||||
ParamInt(string) (int, error)
|
||||
URLParam(string) string
|
||||
|
@ -95,29 +28,38 @@ type (
|
|||
RemoteAddr() string
|
||||
RequestHeader(k string) string
|
||||
PostFormValue(string) string
|
||||
// PostFormMulti returns a slice of string from post request's data
|
||||
PostFormMulti(string) []string
|
||||
}
|
||||
|
||||
// IContextResponse is part of the IContext
|
||||
IContextResponse interface {
|
||||
// SetStatusCode sets the http status code
|
||||
SetStatusCode(int)
|
||||
// SetContentType sets the "Content-Type" header, receives the value
|
||||
SetContentType(string)
|
||||
// SetHeader sets the response headers first parameter is the key, second is the value
|
||||
SetHeader(string, string)
|
||||
Redirect(string, ...int)
|
||||
RedirectTo(routeName string, args ...interface{})
|
||||
// Errors
|
||||
RedirectTo(string, ...interface{})
|
||||
NotFound()
|
||||
Panic()
|
||||
EmitError(int)
|
||||
//
|
||||
}
|
||||
|
||||
// IContextStorage is part of the IContext
|
||||
IContextStorage interface {
|
||||
Write(string, ...interface{})
|
||||
HTML(int, string)
|
||||
Data(int, []byte) error
|
||||
RenderWithStatus(int, string, interface{}, ...string) error
|
||||
Render(string, interface{}, ...string) error
|
||||
MustRender(string, interface{}, ...string)
|
||||
TemplateString(string, interface{}, ...string) string
|
||||
MarkdownString(string) string
|
||||
Markdown(int, string)
|
||||
JSON(int, interface{}) error
|
||||
JSONP(int, string, interface{}) error
|
||||
Text(int, string) error
|
||||
XML(int, interface{}) error
|
||||
ExecuteTemplate(*template.Template, interface{}) error
|
||||
ServeContent(io.ReadSeeker, string, time.Time, bool) error
|
||||
ServeFile(string, bool) error
|
||||
SendFile(string, string) error
|
||||
Stream(func(*bufio.Writer))
|
||||
StreamWriter(cb func(*bufio.Writer))
|
||||
StreamReader(io.Reader, int)
|
||||
ReadJSON(interface{}) error
|
||||
ReadXML(interface{}) error
|
||||
ReadForm(interface{}) error
|
||||
Get(string) interface{}
|
||||
GetString(string) string
|
||||
GetInt(string) int
|
||||
|
@ -125,23 +67,20 @@ type (
|
|||
SetCookie(*fasthttp.Cookie)
|
||||
SetCookieKV(string, string)
|
||||
RemoveCookie(string)
|
||||
// Flash messages
|
||||
GetFlash(string) string
|
||||
GetFlashBytes(string) ([]byte, error)
|
||||
SetFlash(string, string)
|
||||
SetFlashBytes(string, []byte)
|
||||
Session() store.IStore
|
||||
SessionDestroy()
|
||||
}
|
||||
|
||||
// IContextAuth handles the authentication/authorization
|
||||
IContextAuth interface {
|
||||
// SetOAuthUser sets the oauth user
|
||||
// Internal method but exported because useful for advanced use cases
|
||||
// Iris uses this method to set automatically the authenticated user.
|
||||
SetOAuthUser(goth.User)
|
||||
// OAuthUser returns the authenticated User
|
||||
// See https://github.com/iris-contrib/gothic/blob/master/example/main.go to see this in action.
|
||||
OAuthUser() goth.User
|
||||
Log(string, ...interface{})
|
||||
Reset(*fasthttp.RequestCtx)
|
||||
GetRequestCtx() *fasthttp.RequestCtx
|
||||
Clone() IContext
|
||||
Do()
|
||||
Next()
|
||||
StopExecution()
|
||||
IsStopped() bool
|
||||
GetHandlerName() string
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
## Package information
|
||||
|
||||
I decide to split the errors from the main iris package because error.go doesn't depends on any of the iris' types.
|
||||
|
||||
|
||||
**Examples and more info will be added soon.**
|
|
@ -1,72 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/kataras/iris/logger"
|
||||
)
|
||||
|
||||
// Error holds the error
|
||||
type Error struct {
|
||||
message string
|
||||
}
|
||||
|
||||
// Error returns the message of the actual error
|
||||
func (e *Error) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// Format returns a formatted new error based on the arguments
|
||||
func (e *Error) Format(args ...interface{}) error {
|
||||
return fmt.Errorf(e.message, args...)
|
||||
}
|
||||
|
||||
// With does the same thing as Format but it receives an error type which if it's nil it returns a nil error
|
||||
func (e *Error) With(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.Format(err.Error())
|
||||
}
|
||||
|
||||
// Return returns the actual error as it is
|
||||
func (e *Error) Return() error {
|
||||
return e.Format()
|
||||
}
|
||||
|
||||
// Panic output the message and after panics
|
||||
func (e *Error) Panic() {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
errMsg := e.message
|
||||
errMsg = "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
|
||||
panic(errMsg)
|
||||
}
|
||||
|
||||
// Panicf output the formatted message and after panics
|
||||
func (e *Error) Panicf(args ...interface{}) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
errMsg := e.Format(args...).Error()
|
||||
errMsg = "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
|
||||
panic(errMsg)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// New creates and returns an Error with a message
|
||||
func New(errMsg string) *Error {
|
||||
// return &Error{fmt.Errorf("\n" + logger.Prefix + "Error: " + errMsg)}
|
||||
return &Error{message: "\n" + logger.Prefix + " Error: " + errMsg}
|
||||
}
|
||||
|
||||
// Printf prints to the logger a specific error with optionally arguments
|
||||
func Printf(logger *logger.Logger, err error, args ...interface{}) {
|
||||
logger.Printf(err.Error(), args...)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
## Package information
|
||||
|
||||
Enables graceful shutdown.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/graceful"
|
||||
"github.com/kataras/iris"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
api := iris.New()
|
||||
api.Get("/", func(c *iris.Context) {
|
||||
c.Write("Welcome to the home page!")
|
||||
})
|
||||
|
||||
graceful.Run(":3001", time.Duration(10)*time.Second, api)
|
||||
}
|
||||
|
||||
```
|
|
@ -1,291 +0,0 @@
|
|||
package graceful
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"golang.org/x/net/netutil"
|
||||
)
|
||||
|
||||
// Server wraps an iris.Server with graceful connection handling.
|
||||
// It may be used directly in the same way as iris.Server, or may
|
||||
// be constructed with the global functions in this package.
|
||||
type Server struct {
|
||||
*iris.Server
|
||||
station *iris.Framework
|
||||
// Timeout is the duration to allow outstanding requests to survive
|
||||
// before forcefully terminating them.
|
||||
Timeout time.Duration
|
||||
|
||||
// Limit the number of outstanding requests
|
||||
ListenLimit int
|
||||
|
||||
// BeforeShutdown is an optional callback function that is called
|
||||
// before the listener is closed.
|
||||
BeforeShutdown func()
|
||||
|
||||
// ShutdownInitiated is an optional callback function that is called
|
||||
// when shutdown is initiated. It can be used to notify the client
|
||||
// side of long lived connections (e.g. websockets) to reconnect.
|
||||
ShutdownInitiated func()
|
||||
|
||||
// NoSignalHandling prevents graceful from automatically shutting down
|
||||
// on SIGINT and SIGTERM. If set to true, you must shut down the server
|
||||
// manually with Stop().
|
||||
NoSignalHandling bool
|
||||
|
||||
// Logger used to notify of errors on startup and on stop.
|
||||
Logger *logger.Logger
|
||||
|
||||
// Interrupted is true if the server is handling a SIGINT or SIGTERM
|
||||
// signal and is thus shutting down.
|
||||
Interrupted bool
|
||||
|
||||
// interrupt signals the listener to stop serving connections,
|
||||
// and the server to shut down.
|
||||
interrupt chan os.Signal
|
||||
|
||||
// stopLock is used to protect against concurrent calls to Stop
|
||||
stopLock sync.Mutex
|
||||
|
||||
// stopChan is the channel on which callers may block while waiting for
|
||||
// the server to stop.
|
||||
stopChan chan struct{}
|
||||
|
||||
// chanLock is used to protect access to the various channel constructors.
|
||||
chanLock sync.RWMutex
|
||||
|
||||
// connections holds all connections managed by graceful
|
||||
connections map[net.Conn]struct{}
|
||||
}
|
||||
|
||||
// Run serves the iris.Handler with graceful shutdown enabled.
|
||||
//
|
||||
// timeout is the duration to wait until killing active requests and stopping the server.
|
||||
// If timeout is 0, the server never times out. It waits for all active requests to finish.
|
||||
// we don't pass an iris.RequestHandler , because we need iris.station.server to be setted in order the station.Close() to work
|
||||
func Run(addr string, timeout time.Duration, s *iris.Framework) {
|
||||
s.HTTPServer.Config.ListeningAddr = addr
|
||||
srv := &Server{
|
||||
Timeout: timeout,
|
||||
Logger: s.Logger,
|
||||
station: s,
|
||||
Server: s.NoListen(),
|
||||
}
|
||||
if err := srv.listenAndServe(); err != nil {
|
||||
if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
|
||||
srv.Logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// RunWithErr is an alternative version of Run function which can return error.
|
||||
//
|
||||
// Unlike Run this version will not exit the program if an error is encountered but will
|
||||
// return it instead.
|
||||
func RunWithErr(addr string, timeout time.Duration, s *iris.Framework) error {
|
||||
s.HTTPServer.Config.ListeningAddr = addr
|
||||
srv := &Server{
|
||||
Timeout: timeout,
|
||||
Logger: s.Logger,
|
||||
station: s,
|
||||
Server: s.NoListen(),
|
||||
}
|
||||
|
||||
return srv.listenAndServe()
|
||||
}
|
||||
|
||||
// ListenAndServe is equivalent to iris.Listen with graceful shutdown enabled.
|
||||
func (srv *Server) listenAndServe() error {
|
||||
// Create the listener so we can control their lifetime
|
||||
addr := srv.station.HTTPServer.Config.ListeningAddr
|
||||
if addr == "" {
|
||||
addr = ":http"
|
||||
}
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.serve(l)
|
||||
}
|
||||
|
||||
// Serve is equivalent to iris.Server.Serve with graceful shutdown enabled.
|
||||
func (srv *Server) serve(listener net.Listener) error {
|
||||
|
||||
if srv.ListenLimit != 0 {
|
||||
listener = netutil.LimitListener(listener, srv.ListenLimit)
|
||||
}
|
||||
|
||||
// Track connection state
|
||||
add := make(chan net.Conn)
|
||||
remove := make(chan net.Conn)
|
||||
|
||||
// Manage open connections
|
||||
shutdown := make(chan chan struct{})
|
||||
kill := make(chan struct{})
|
||||
go srv.manageConnections(add, remove, shutdown, kill)
|
||||
|
||||
interrupt := srv.interruptChan()
|
||||
// Set up the interrupt handler
|
||||
if !srv.NoSignalHandling {
|
||||
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
}
|
||||
quitting := make(chan struct{})
|
||||
go srv.handleInterrupt(interrupt, quitting, listener)
|
||||
|
||||
// Serve with graceful listener.
|
||||
// Execution blocks here until listener.Close() is called, above.
|
||||
err := srv.Server.Serve(listener)
|
||||
if err != nil {
|
||||
// If the underlying listening is closed, Serve returns an error
|
||||
// complaining about listening on a closed socket. This is expected, so
|
||||
// let's ignore the error if we are the ones who explicitly closed the
|
||||
// socket.
|
||||
select {
|
||||
case <-quitting:
|
||||
err = nil
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
srv.shutdown(shutdown, kill)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop instructs the type to halt operations and close
|
||||
// the stop channel when it is finished.
|
||||
//
|
||||
// timeout is grace period for which to wait before shutting
|
||||
// down the server. The timeout value passed here will override the
|
||||
// timeout given when constructing the server, as this is an explicit
|
||||
// command to stop the server.
|
||||
func (srv *Server) Stop(timeout time.Duration) {
|
||||
srv.stopLock.Lock()
|
||||
defer srv.stopLock.Unlock()
|
||||
|
||||
srv.Timeout = timeout
|
||||
interrupt := srv.interruptChan()
|
||||
interrupt <- syscall.SIGINT
|
||||
}
|
||||
|
||||
// StopChan gets the stop channel which will block until
|
||||
// stopping has completed, at which point it is closed.
|
||||
// Callers should never close the stop channel.
|
||||
func (srv *Server) StopChan() <-chan struct{} {
|
||||
srv.chanLock.Lock()
|
||||
defer srv.chanLock.Unlock()
|
||||
|
||||
if srv.stopChan == nil {
|
||||
srv.stopChan = make(chan struct{})
|
||||
}
|
||||
return srv.stopChan
|
||||
}
|
||||
|
||||
// DefaultLogger returns the logger used by Run, RunWithErr, ListenAndServe, ListenAndServeTLS and Serve.
|
||||
// The logger outputs to STDOUT by default.
|
||||
func DefaultLogger() *logger.Logger {
|
||||
return logger.New(config.DefaultLogger())
|
||||
}
|
||||
|
||||
func (srv *Server) manageConnections(add, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) {
|
||||
var done chan struct{}
|
||||
srv.connections = map[net.Conn]struct{}{}
|
||||
for {
|
||||
select {
|
||||
case conn := <-add:
|
||||
srv.connections[conn] = struct{}{}
|
||||
case conn := <-remove:
|
||||
delete(srv.connections, conn)
|
||||
if done != nil && len(srv.connections) == 0 {
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
case done = <-shutdown:
|
||||
if len(srv.connections) == 0 {
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
case <-kill:
|
||||
for k := range srv.connections {
|
||||
if err := k.Close(); err != nil {
|
||||
srv.log("[IRIS GRACEFUL ERROR]" + err.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) interruptChan() chan os.Signal {
|
||||
srv.chanLock.Lock()
|
||||
defer srv.chanLock.Unlock()
|
||||
|
||||
if srv.interrupt == nil {
|
||||
srv.interrupt = make(chan os.Signal, 1)
|
||||
}
|
||||
|
||||
return srv.interrupt
|
||||
}
|
||||
|
||||
func (srv *Server) handleInterrupt(interrupt chan os.Signal, quitting chan struct{}, listener net.Listener) {
|
||||
for range interrupt {
|
||||
if srv.Interrupted {
|
||||
srv.log("already shutting down")
|
||||
continue
|
||||
}
|
||||
srv.log("shutdown initiated")
|
||||
srv.Interrupted = true
|
||||
if srv.BeforeShutdown != nil {
|
||||
srv.BeforeShutdown()
|
||||
}
|
||||
|
||||
close(quitting)
|
||||
srv.Server.DisableKeepalive = true
|
||||
if err := listener.Close(); err != nil {
|
||||
srv.log("[IRIS GRACEFUL ERROR]" + err.Error())
|
||||
}
|
||||
|
||||
if srv.ShutdownInitiated != nil {
|
||||
srv.ShutdownInitiated()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) log(fmt string, v ...interface{}) {
|
||||
if srv.Logger != nil {
|
||||
srv.Logger.Printf(fmt, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) {
|
||||
// Request done notification
|
||||
done := make(chan struct{})
|
||||
shutdown <- done
|
||||
|
||||
if srv.Timeout > 0 {
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(srv.Timeout):
|
||||
close(kill)
|
||||
}
|
||||
} else {
|
||||
<-done
|
||||
}
|
||||
// Close the stopChan to wake up any blocked goroutines.
|
||||
srv.chanLock.Lock()
|
||||
if srv.stopChan != nil {
|
||||
close(srv.stopChan)
|
||||
}
|
||||
// notify the iris plugins
|
||||
srv.station.Close()
|
||||
srv.chanLock.Unlock()
|
||||
}
|
4
http.go
4
http.go
|
@ -10,8 +10,8 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
|
@ -296,7 +296,7 @@ func (s *Server) VirtualHost() (host string) {
|
|||
return s.Config.ListeningAddr
|
||||
}
|
||||
|
||||
// Fullhost returns the scheme+host
|
||||
// FullHost returns the scheme+host
|
||||
func (s *Server) FullHost() string {
|
||||
scheme := "http://"
|
||||
// we need to be able to take that before(for testing &debugging) and after server's listen
|
||||
|
|
|
@ -6,13 +6,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/gothic"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/websocket"
|
||||
"github.com/markbates/goth"
|
||||
|
||||
"github.com/kataras/iris/mail"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
"github.com/kataras/iris/render/template"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
@ -70,11 +67,9 @@ const (
|
|||
// Implements the FrameworkAPI
|
||||
type Framework struct {
|
||||
*muxAPI
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
mailer mail.Service
|
||||
oauthHandlers Middleware
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
// fields which are useful to the user/dev
|
||||
HTTPServer *Server
|
||||
Config *config.Iris
|
||||
|
@ -121,45 +116,9 @@ func (s *Framework) initialize() {
|
|||
// set the rest
|
||||
s.rest = rest.New(s.Config.Render.Rest)
|
||||
|
||||
// set mail and templates if not already setted
|
||||
s.prepareMailer()
|
||||
// set templates if not already setted
|
||||
s.prepareTemplates()
|
||||
|
||||
// set the oauth providers from the OAuth configuration field
|
||||
|
||||
// the user still can set his/her own provider (using goth.UseProviders), if the configuration for the provider is not exists
|
||||
// prepare the configs
|
||||
s.Config.OAuth = config.DefaultOAuth().MergeSingle(s.Config.OAuth)
|
||||
oauthProviders := s.Config.OAuth.GetAll(s.HTTPServer.FullHost())
|
||||
if len(oauthProviders) > 0 {
|
||||
goth.UseProviders(oauthProviders...)
|
||||
// set the mux path to handle these providers
|
||||
s.Get(s.Config.OAuth.Path+"/:provider", func(ctx *Context) {
|
||||
err := gothic.BeginAuthHandler(ctx)
|
||||
if err != nil {
|
||||
s.Logger.Warningf("\n[IRIS: OAUTH] Error:" + err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
authMiddleware := func(ctx *Context) {
|
||||
|
||||
user, err := gothic.CompleteUserAuth(ctx)
|
||||
if err != nil {
|
||||
ctx.EmitError(StatusUnauthorized)
|
||||
ctx.Log(err.Error())
|
||||
return
|
||||
}
|
||||
ctx.SetOAuthUser(user)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
s.oauthHandlers = append([]Handler{HandlerFunc(authMiddleware)}, s.oauthHandlers...)
|
||||
|
||||
s.Handle(MethodGet, s.Config.OAuth.Path+"/:provider/callback", s.oauthHandlers...)("oauth")
|
||||
}
|
||||
|
||||
// end of auth
|
||||
|
||||
// listen to websocket connections
|
||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
||||
|
||||
|
@ -177,14 +136,6 @@ func (s *Framework) initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
// prepareMailer sets the mailer if not nil, we make this check because of .SendMail, which can be called before Listen
|
||||
func (s *Framework) prepareMailer() {
|
||||
// prepare the mail service
|
||||
if s.mailer == nil {
|
||||
s.mailer = mail.New(s.Config.Mail)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
|
||||
func (s *Framework) prepareTemplates() {
|
||||
// prepare the templates
|
||||
|
|
33
iris.go
33
iris.go
|
@ -59,16 +59,16 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version of the iris
|
||||
Version = "3.0.0-rc.2"
|
||||
Version = "3.0.0-rc.3"
|
||||
banner = ` _____ _
|
||||
|_ _| (_)
|
||||
| | ____ _ ___
|
||||
|
@ -96,12 +96,10 @@ type (
|
|||
MustUseFunc(...HandlerFunc)
|
||||
OnError(int, HandlerFunc)
|
||||
EmitError(int, *Context)
|
||||
OnUserOAuth(...HandlerFunc)
|
||||
Lookup(string) Route
|
||||
Lookups() []Route
|
||||
Path(string, ...interface{}) string
|
||||
URL(string, ...interface{}) string
|
||||
SendMail(string, string, ...string) error
|
||||
TemplateString(string, interface{}, ...string) string
|
||||
}
|
||||
|
||||
|
@ -538,19 +536,6 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
|
|||
return
|
||||
}
|
||||
|
||||
// SendMail sends a mail to recipients
|
||||
// the body can be html also
|
||||
func SendMail(subject string, body string, to ...string) error {
|
||||
return Default.SendMail(subject, body, to...)
|
||||
}
|
||||
|
||||
// SendMail sends a mail to recipients
|
||||
// the body can be html also
|
||||
func (s *Framework) SendMail(subject string, body string, to ...string) error {
|
||||
s.prepareMailer()
|
||||
return s.mailer.Send(subject, body, to...)
|
||||
}
|
||||
|
||||
// TemplateString executes a template and returns its result as string, useful when you want it for sending rich e-mails
|
||||
// returns empty string on error
|
||||
func TemplateString(templateFile string, pageContext interface{}, layout ...string) string {
|
||||
|
@ -568,20 +553,6 @@ func (s *Framework) TemplateString(templateFile string, pageContext interface{},
|
|||
return res
|
||||
}
|
||||
|
||||
/* Auth */
|
||||
|
||||
// OnUserOAuth fires the middleware when the user logged in successfully via gothic oauth
|
||||
// get the user using the context.OAuthUser()
|
||||
func OnUserOAuth(handlersFn ...HandlerFunc) {
|
||||
Default.OnUserOAuth(handlersFn...)
|
||||
}
|
||||
|
||||
// OnUserOAuth fires the middleware when the user logged in successfully via gothic oauth
|
||||
// get the user using the context.OAuthUser()
|
||||
func (s *Framework) OnUserOAuth(handlersFn ...HandlerFunc) {
|
||||
s.oauthHandlers = append(s.oauthHandlers, convertToHandlers(handlersFn)...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------------------
|
||||
// ----------------------------------MuxAPI implementation------------------------------
|
||||
|
|
13
iris/run.go
13
iris/run.go
|
@ -8,8 +8,8 @@ import (
|
|||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/cli"
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
|
@ -71,10 +71,21 @@ func runAndWatch(flags cli.Flags) error {
|
|||
if filepath.Ext(programPath) != goExt {
|
||||
return errInvalidExt.Format(programPath)
|
||||
}
|
||||
|
||||
// check if we have a path,change the workingdir and programpath
|
||||
if lidx := strings.LastIndexByte(programPath, os.PathSeparator); lidx > 0 { // no /
|
||||
workingDir = workingDir + utils.PathSeparator + programPath[0:lidx]
|
||||
programPath = programPath[lidx+1:]
|
||||
} else if lidx := strings.LastIndexByte(programPath, '/'); lidx > 0 { // no /
|
||||
workingDir = workingDir + "/" + programPath[0:lidx]
|
||||
programPath = programPath[lidx+1:]
|
||||
}
|
||||
|
||||
executablePath = programPath[:len(programPath)-3]
|
||||
if isWindows {
|
||||
executablePath += ".exe"
|
||||
}
|
||||
|
||||
}
|
||||
// here(below), we don't return the error because the -help command doesn't help the user for these errors.
|
||||
|
||||
|
|
|
@ -26,11 +26,19 @@ func attr(sgr int) color.Attribute {
|
|||
return color.Attribute(sgr)
|
||||
}
|
||||
|
||||
// check if background color > 0 and if so then set it
|
||||
func (l *Logger) setBg(sgr int) {
|
||||
if sgr > 0 {
|
||||
l.underline.Add(attr(sgr))
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Logger from config.Logger configuration
|
||||
func New(c config.Logger) *Logger {
|
||||
color.Output = colorable.NewColorable(c.Out)
|
||||
|
||||
l := &Logger{&c, color.New(attr(c.ColorBgDefault), attr(c.ColorFgDefault), color.Bold)}
|
||||
l := &Logger{&c, color.New(attr(c.ColorFgDefault))}
|
||||
l.setBg(c.ColorBgDefault)
|
||||
|
||||
return l
|
||||
}
|
||||
|
@ -48,19 +56,25 @@ func (l *Logger) IsEnabled() bool {
|
|||
// ResetColors sets the colors to the default
|
||||
// this func is called every time a success, info, warning, or danger message is printed
|
||||
func (l *Logger) ResetColors() {
|
||||
l.underline.Add(attr(l.config.ColorBgDefault), attr(l.config.ColorFgDefault))
|
||||
l.underline.Add(attr(l.config.ColorFgDefault))
|
||||
l.setBg(l.config.ColorBgDefault)
|
||||
}
|
||||
|
||||
// PrintBanner prints a text (banner) with BannerFgColor, BannerBgColor and a success message at the end
|
||||
// It doesn't cares if the logger is disabled or not, it will print this
|
||||
func (l *Logger) PrintBanner(banner string, successMessage string) {
|
||||
c := color.New(attr(l.config.ColorBgDefault), attr(l.config.ColorFgBanner), color.Bold)
|
||||
c := color.New(attr(l.config.ColorFgBanner))
|
||||
if l.config.ColorBgDefault > 0 {
|
||||
c.Add(attr(l.config.ColorBgDefault))
|
||||
}
|
||||
c.Println(banner)
|
||||
bannersRan++
|
||||
|
||||
if successMessage != "" {
|
||||
c.Add(attr(l.config.ColorBgSuccess), attr(l.config.ColorFgSuccess), color.Bold)
|
||||
|
||||
c.Add(attr(l.config.ColorFgSuccess))
|
||||
if l.config.ColorBgSuccess > 0 {
|
||||
c.Add(attr(l.config.ColorBgSuccess))
|
||||
}
|
||||
if bannersRan > 1 {
|
||||
c.Printf("Server[%#v]\n", bannersRan)
|
||||
|
||||
|
@ -125,7 +139,8 @@ func (l *Logger) Panicf(format string, a ...interface{}) {
|
|||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Successf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgSuccess), attr(l.config.ColorFgSuccess))
|
||||
l.underline.Add(attr(l.config.ColorFgSuccess))
|
||||
l.setBg(l.config.ColorBgSuccess)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +149,8 @@ func (l *Logger) Successf(format string, a ...interface{}) {
|
|||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Infof(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgInfo), attr(l.config.ColorFgInfo))
|
||||
l.underline.Add(attr(l.config.ColorFgInfo))
|
||||
l.setBg(l.config.ColorBgInfo)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +159,8 @@ func (l *Logger) Infof(format string, a ...interface{}) {
|
|||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Warningf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgWarning), attr(l.config.ColorFgWarning))
|
||||
l.underline.Add(attr(l.config.ColorFgWarning))
|
||||
l.setBg(l.config.ColorBgWarning)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +169,8 @@ func (l *Logger) Warningf(format string, a ...interface{}) {
|
|||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Dangerf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgDanger), attr(l.config.ColorFgDanger))
|
||||
l.underline.Add(attr(l.config.ColorFgDanger))
|
||||
l.setBg(l.config.ColorBgDanger)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +179,8 @@ func (l *Logger) Dangerf(format string, a ...interface{}) {
|
|||
// Arguments are handled in the manner of fmt.Printf.
|
||||
func (l *Logger) Otherf(format string, a ...interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgOther), attr(l.config.ColorFgOther))
|
||||
l.underline.Add(attr(l.config.ColorFgOther))
|
||||
l.setBg(l.config.ColorBgOther)
|
||||
l.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
|
123
mail/service.go
123
mail/service.go
|
@ -1,123 +0,0 @@
|
|||
package mail
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
var buf = utils.NewBufferPool(64)
|
||||
var once sync.Once
|
||||
|
||||
type (
|
||||
// Service is the interface which mail sender should implement
|
||||
Service interface {
|
||||
// Send sends a mail to recipients
|
||||
// the body can be html also
|
||||
Send(string, string, ...string) error
|
||||
UpdateConfig(config.Mail)
|
||||
}
|
||||
|
||||
mailer struct {
|
||||
config *config.Mail
|
||||
fromAddr mail.Address
|
||||
auth smtp.Auth
|
||||
authenticated bool
|
||||
}
|
||||
)
|
||||
|
||||
// New creates and returns a new Service
|
||||
func New(cfg config.Mail) Service {
|
||||
m := &mailer{config: &cfg}
|
||||
if cfg.FromAlias == "" {
|
||||
if !cfg.UseCommand && cfg.Username != "" && strings.Contains(cfg.Username, "@") {
|
||||
m.fromAddr = mail.Address{Name: cfg.Username[0:strings.IndexByte(cfg.Username, '@')], Address: cfg.Username}
|
||||
}
|
||||
} else {
|
||||
m.fromAddr = mail.Address{Name: cfg.FromAlias, Address: cfg.Username}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *mailer) UpdateConfig(cfg config.Mail) {
|
||||
m.config = &cfg
|
||||
}
|
||||
|
||||
// Send sends a mail to recipients
|
||||
// the body can be html also
|
||||
func (m *mailer) Send(subject string, body string, to ...string) error {
|
||||
if m.config.UseCommand {
|
||||
return m.sendCmd(subject, body, to)
|
||||
}
|
||||
|
||||
return m.sendSMTP(subject, body, to)
|
||||
}
|
||||
|
||||
func (m *mailer) sendSMTP(subject string, body string, to []string) error {
|
||||
buffer := buf.Get()
|
||||
defer buf.Put(buffer)
|
||||
|
||||
if !m.authenticated {
|
||||
cfg := m.config
|
||||
if cfg.Username == "" || cfg.Password == "" || cfg.Host == "" || cfg.Port <= 0 {
|
||||
return fmt.Errorf("Username, Password, Host & Port cannot be empty when using SMTP!")
|
||||
}
|
||||
m.auth = smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host)
|
||||
m.authenticated = true
|
||||
}
|
||||
|
||||
fullhost := fmt.Sprintf("%s:%d", m.config.Host, m.config.Port)
|
||||
|
||||
header := make(map[string]string)
|
||||
header["From"] = m.fromAddr.String()
|
||||
header["To"] = strings.Join(to, ",")
|
||||
header["Subject"] = subject
|
||||
header["MIME-Version"] = "1.0"
|
||||
header["Content-Type"] = "text/html; charset=\"utf-8\""
|
||||
header["Content-Transfer-Encoding"] = "base64"
|
||||
|
||||
message := ""
|
||||
for k, v := range header {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
||||
|
||||
return smtp.SendMail(
|
||||
fmt.Sprintf(fullhost),
|
||||
m.auth,
|
||||
m.config.Username,
|
||||
to,
|
||||
[]byte(message),
|
||||
)
|
||||
}
|
||||
|
||||
func (m *mailer) sendCmd(subject string, body string, to []string) error {
|
||||
buffer := buf.Get()
|
||||
defer buf.Put(buffer)
|
||||
|
||||
header := make(map[string]string)
|
||||
header["To"] = strings.Join(to, ",")
|
||||
header["Subject"] = subject
|
||||
header["MIME-Version"] = "1.0"
|
||||
header["Content-Type"] = "text/html; charset=\"utf-8\""
|
||||
header["Content-Transfer-Encoding"] = "base64"
|
||||
|
||||
message := ""
|
||||
for k, v := range header {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
||||
buffer.WriteString(message)
|
||||
// fix by @qskousen
|
||||
cmd := utils.CommandBuilder("sendmail", "-F", m.fromAddr.Name, "-f", m.fromAddr.Address, "-t")
|
||||
|
||||
cmd.Stdin = buffer
|
||||
_, err := cmd.CombinedOutput()
|
||||
return err
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# Middleware
|
||||
Iris has its tiny middlewares here.
|
|
@ -1,157 +0,0 @@
|
|||
package basicauth
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
type (
|
||||
encodedUser struct {
|
||||
HeaderValue string
|
||||
Username string
|
||||
logged bool
|
||||
expires time.Time
|
||||
}
|
||||
encodedUsers []encodedUser
|
||||
|
||||
basicAuthMiddleware struct {
|
||||
config config.BasicAuth
|
||||
// these are filled from the config.Users map at the startup
|
||||
auth encodedUsers
|
||||
realmHeaderValue string
|
||||
expireEnabled bool // if the config.Expires is a valid date, default disabled
|
||||
}
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
// New takes one parameter, the config.BasicAuth returns a HandlerFunc
|
||||
// use: iris.UseFunc(New(...)), iris.Get(...,New(...),...)
|
||||
func New(c config.BasicAuth) iris.HandlerFunc {
|
||||
return NewHandler(c).Serve
|
||||
}
|
||||
|
||||
// NewHandler takes one parameter, the config.BasicAuth returns a Handler
|
||||
// use: iris.Use(NewHandler(...)), iris.Get(...,iris.HandlerFunc(NewHandler(...)),...)
|
||||
func NewHandler(c config.BasicAuth) iris.Handler {
|
||||
b := &basicAuthMiddleware{config: config.DefaultBasicAuth().MergeSingle(c)}
|
||||
b.init()
|
||||
return b
|
||||
}
|
||||
|
||||
// Default takes one parameter, the users returns a HandlerFunc
|
||||
// use: iris.UseFunc(Default(...)), iris.Get(...,Default(...),...)
|
||||
func Default(users map[string]string) iris.HandlerFunc {
|
||||
return DefaultHandler(users).Serve
|
||||
}
|
||||
|
||||
// DefaultHandler takes one parameter, the users returns a Handler
|
||||
// use: iris.Use(DefaultHandler(...)), iris.Get(...,iris.HandlerFunc(Default(...)),...)
|
||||
func DefaultHandler(users map[string]string) iris.Handler {
|
||||
c := config.DefaultBasicAuth()
|
||||
c.Users = users
|
||||
return NewHandler(c)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
func (b *basicAuthMiddleware) init() {
|
||||
// pass the encoded users from the user's config's Users value
|
||||
b.auth = make(encodedUsers, 0, len(b.config.Users))
|
||||
|
||||
for k, v := range b.config.Users {
|
||||
fullUser := k + ":" + v
|
||||
header := "Basic " + base64.StdEncoding.EncodeToString([]byte(fullUser))
|
||||
b.auth = append(b.auth, encodedUser{HeaderValue: header, Username: k, logged: false, expires: config.CookieExpireNever})
|
||||
}
|
||||
|
||||
// set the auth realm header's value
|
||||
b.realmHeaderValue = "Basic realm=" + strconv.Quote(b.config.Realm)
|
||||
|
||||
if b.config.Expires > 0 {
|
||||
b.expireEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *basicAuthMiddleware) findAuth(headerValue string) (auth *encodedUser, found bool) {
|
||||
if len(headerValue) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range b.auth {
|
||||
if user.HeaderValue == headerValue {
|
||||
auth = &user
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (b *basicAuthMiddleware) askForCredentials(ctx *iris.Context) {
|
||||
ctx.SetHeader("WWW-Authenticate", b.realmHeaderValue)
|
||||
ctx.SetStatusCode(iris.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// Serve the actual middleware
|
||||
func (b *basicAuthMiddleware) Serve(ctx *iris.Context) {
|
||||
|
||||
if auth, found := b.findAuth(ctx.RequestHeader("Authorization")); !found {
|
||||
/* I spent time for nothing
|
||||
if b.banEnabled && auth != nil { // this propably never work
|
||||
|
||||
if auth.tries == b.config.MaxTries {
|
||||
auth.bannedTime = time.Now()
|
||||
auth.unbanTime = time.Now().Add(b.config.BanDuration) // set the unban time
|
||||
auth.tries++ // we plus them in order to check if already banned later
|
||||
// client is banned send a forbidden status and don't continue
|
||||
ctx.SetStatusCode(iris.StatusForbidden)
|
||||
return
|
||||
} else if auth.tries > b.config.MaxTries { // it's already banned, so check the ban duration with the bannedTime
|
||||
if time.Now().After(auth.unbanTime) { // here we unban the client
|
||||
auth.tries = 0
|
||||
auth.bannedTime = config.CookieExpireNever
|
||||
auth.unbanTime = config.CookieExpireNever
|
||||
// continue and askCredentials as normal
|
||||
} else {
|
||||
// client is banned send a forbidden status and don't continue
|
||||
ctx.SetStatusCode(iris.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if auth != nil {
|
||||
auth.tries++
|
||||
}*/
|
||||
|
||||
b.askForCredentials(ctx)
|
||||
// don't continue to the next handler
|
||||
} else {
|
||||
// all ok set the context's value in order to be getable from the next handler
|
||||
ctx.Set(b.config.ContextKey, auth.Username)
|
||||
if b.expireEnabled {
|
||||
|
||||
if auth.logged == false {
|
||||
auth.expires = time.Now().Add(b.config.Expires)
|
||||
auth.logged = true
|
||||
}
|
||||
|
||||
if time.Now().After(auth.expires) {
|
||||
b.askForCredentials(ctx) // ask for authentication again
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//auth.tries = 0
|
||||
ctx.Next() // continue
|
||||
}
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
## Middleware information
|
||||
|
||||
This is a fork of the CORS middleware from [here](https://github.com/rs/cors/)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
It does some security work for you between the requests, a brief view on what you can set:
|
||||
|
||||
* AllowedOrigins []string
|
||||
|
||||
* AllowOriginFunc func(origin string) bool
|
||||
|
||||
* AllowedMethods []string
|
||||
|
||||
* AllowedHeadersAll bool
|
||||
|
||||
* ExposedHeaders []string
|
||||
|
||||
* AllowCredentials bool
|
||||
|
||||
* MaxAge int
|
||||
|
||||
* OptionsPassthrough bool
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
```go
|
||||
|
||||
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
|
||||
// If the special "*" value is present in the list, all origins will be allowed.
|
||||
// An origin may contain a wildcard (*) to replace 0 or more characters
|
||||
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
|
||||
// Only one wildcard can be used per origin.
|
||||
// Default value is ["*"]
|
||||
AllowedOrigins []string
|
||||
// AllowOriginFunc is a custom function to validate the origin. It take the origin
|
||||
// as argument and returns true if allowed or false otherwise. If this option is
|
||||
// set, the content of AllowedOrigins is ignored.
|
||||
AllowOriginFunc func(origin string) bool
|
||||
// AllowedMethods is a list of methods the client is allowed to use with
|
||||
// cross-domain requests. Default value is simple methods (GET and POST)
|
||||
AllowedMethods []string
|
||||
// AllowedHeaders is list of non simple headers the client is allowed to use with
|
||||
// cross-domain requests.
|
||||
// If the special "*" value is present in the list, all headers will be allowed.
|
||||
// Default value is [] but "Origin" is always appended to the list.
|
||||
AllowedHeaders []string
|
||||
|
||||
AllowedHeadersAll bool
|
||||
|
||||
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
|
||||
// API specification
|
||||
ExposedHeaders []string
|
||||
// AllowCredentials indicates whether the request can include user credentials like
|
||||
// cookies, HTTP authentication or client side SSL certificates.
|
||||
AllowCredentials bool
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached
|
||||
MaxAge int
|
||||
// OptionsPassthrough instructs preflight to let other potential next handlers to
|
||||
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
|
||||
OptionsPassthrough bool
|
||||
// Debugging flag adds additional output to debug server side CORS issues
|
||||
Debug bool
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/cors"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
//crs := cors.New(cors.Options{})
|
||||
|
||||
iris.Use(cors.Default()) // crs
|
||||
|
||||
iris.Get("/home", func(c *iris.Context) {
|
||||
c.Write("Hello from /home")
|
||||
})
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
|
@ -1,405 +0,0 @@
|
|||
// Cors credits goes to @rs
|
||||
|
||||
package cors
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
const toLower = 'a' - 'A'
|
||||
|
||||
type converter func(string) string
|
||||
|
||||
type wildcard struct {
|
||||
prefix string
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (w wildcard) match(s string) bool {
|
||||
return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
|
||||
}
|
||||
|
||||
// convert converts a list of string using the passed converter function
|
||||
func convert(s []string, c converter) []string {
|
||||
out := []string{}
|
||||
for _, i := range s {
|
||||
out = append(out, c(i))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// parseHeaderList tokenize + normalize a string containing a list of headers
|
||||
func parseHeaderList(headerList string) []string {
|
||||
l := len(headerList)
|
||||
h := make([]byte, 0, l)
|
||||
upper := true
|
||||
// Estimate the number headers in order to allocate the right splice size
|
||||
t := 0
|
||||
for i := 0; i < l; i++ {
|
||||
if headerList[i] == ',' {
|
||||
t++
|
||||
}
|
||||
}
|
||||
headers := make([]string, 0, t)
|
||||
for i := 0; i < l; i++ {
|
||||
b := headerList[i]
|
||||
if b >= 'a' && b <= 'z' {
|
||||
if upper {
|
||||
h = append(h, b-toLower)
|
||||
} else {
|
||||
h = append(h, b)
|
||||
}
|
||||
} else if b >= 'A' && b <= 'Z' {
|
||||
if !upper {
|
||||
h = append(h, b+toLower)
|
||||
} else {
|
||||
h = append(h, b)
|
||||
}
|
||||
} else if b == '-' || b == '_' || (b >= '0' && b <= '9') {
|
||||
h = append(h, b)
|
||||
}
|
||||
|
||||
if b == ' ' || b == ',' || i == l-1 {
|
||||
if len(h) > 0 {
|
||||
// Flush the found header
|
||||
headers = append(headers, string(h))
|
||||
h = h[:0]
|
||||
upper = true
|
||||
}
|
||||
} else {
|
||||
upper = b == '-' || b == '_'
|
||||
}
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
// Options is a configuration container to setup the CORS middleware.
|
||||
type Options struct {
|
||||
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
|
||||
// If the special "*" value is present in the list, all origins will be allowed.
|
||||
// An origin may contain a wildcard (*) to replace 0 or more characters
|
||||
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
|
||||
// Only one wildcard can be used per origin.
|
||||
// Default value is ["*"]
|
||||
AllowedOrigins []string
|
||||
// AllowOriginFunc is a custom function to validate the origin. It take the origin
|
||||
// as argument and returns true if allowed or false otherwise. If this option is
|
||||
// set, the content of AllowedOrigins is ignored.
|
||||
AllowOriginFunc func(origin string) bool
|
||||
// AllowedMethods is a list of methods the client is allowed to use with
|
||||
// cross-domain requests. Default value is simple methods (GET and POST)
|
||||
AllowedMethods []string
|
||||
// AllowedHeaders is list of non simple headers the client is allowed to use with
|
||||
// cross-domain requests.
|
||||
// If the special "*" value is present in the list, all headers will be allowed.
|
||||
// Default value is [] but "Origin" is always appended to the list.
|
||||
AllowedHeaders []string
|
||||
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
|
||||
// API specification
|
||||
ExposedHeaders []string
|
||||
// AllowCredentials indicates whether the request can include user credentials like
|
||||
// cookies, HTTP authentication or client side SSL certificates.
|
||||
AllowCredentials bool
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached
|
||||
MaxAge int
|
||||
// OptionsPassthrough instructs preflight to let other potential next handlers to
|
||||
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
|
||||
OptionsPassthrough bool
|
||||
// Debugging flag adds additional output to debug server side CORS issues
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// Cors http handler
|
||||
type Cors struct {
|
||||
// Debug logger
|
||||
Log *log.Logger
|
||||
// Set to true when allowed origins contains a "*"
|
||||
allowedOriginsAll bool
|
||||
// Normalized list of plain allowed origins
|
||||
allowedOrigins []string
|
||||
// List of allowed origins containing wildcards
|
||||
allowedWOrigins []wildcard
|
||||
// Optional origin validator function
|
||||
allowOriginFunc func(origin string) bool
|
||||
// Set to true when allowed headers contains a "*"
|
||||
allowedHeadersAll bool
|
||||
// Normalized list of allowed headers
|
||||
allowedHeaders []string
|
||||
// Normalized list of allowed methods
|
||||
allowedMethods []string
|
||||
// Normalized list of exposed headers
|
||||
exposedHeaders []string
|
||||
allowCredentials bool
|
||||
maxAge int
|
||||
optionPassthrough bool
|
||||
}
|
||||
|
||||
// New creates a new Cors handler with the provided options.
|
||||
func New(options Options) *Cors {
|
||||
c := &Cors{
|
||||
exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
|
||||
allowOriginFunc: options.AllowOriginFunc,
|
||||
allowCredentials: options.AllowCredentials,
|
||||
maxAge: options.MaxAge,
|
||||
optionPassthrough: options.OptionsPassthrough,
|
||||
}
|
||||
if options.Debug {
|
||||
c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
|
||||
}
|
||||
|
||||
// Normalize options
|
||||
// Note: for origins and methods matching, the spec requires a case-sensitive matching.
|
||||
// As it may error prone, we chose to ignore the spec here.
|
||||
|
||||
// Allowed Origins
|
||||
if len(options.AllowedOrigins) == 0 {
|
||||
// Default is all origins
|
||||
c.allowedOriginsAll = true
|
||||
} else {
|
||||
c.allowedOrigins = []string{}
|
||||
c.allowedWOrigins = []wildcard{}
|
||||
for _, origin := range options.AllowedOrigins {
|
||||
// Normalize
|
||||
origin = strings.ToLower(origin)
|
||||
if origin == "*" {
|
||||
// If "*" is present in the list, turn the whole list into a match all
|
||||
c.allowedOriginsAll = true
|
||||
c.allowedOrigins = nil
|
||||
c.allowedWOrigins = nil
|
||||
break
|
||||
} else if i := strings.IndexByte(origin, '*'); i >= 0 {
|
||||
// Split the origin in two: start and end string without the *
|
||||
w := wildcard{origin[0:i], origin[i+1:]}
|
||||
c.allowedWOrigins = append(c.allowedWOrigins, w)
|
||||
} else {
|
||||
c.allowedOrigins = append(c.allowedOrigins, origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allowed Headers
|
||||
if len(options.AllowedHeaders) == 0 {
|
||||
// Use sensible defaults
|
||||
c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
|
||||
} else {
|
||||
// Origin is always appended as some browsers will always request for this header at preflight
|
||||
c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
|
||||
for _, h := range options.AllowedHeaders {
|
||||
if h == "*" {
|
||||
c.allowedHeadersAll = true
|
||||
c.allowedHeaders = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allowed Methods
|
||||
if len(options.AllowedMethods) == 0 {
|
||||
// Default is spec's "simple" methods
|
||||
c.allowedMethods = []string{"GET", "POST"}
|
||||
} else {
|
||||
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Default creates a new Cors handler with default options
|
||||
func Default() *Cors {
|
||||
return New(Options{})
|
||||
}
|
||||
|
||||
// DefaultCors creates a new Cors handler with default options
|
||||
func DefaultCors() *Cors {
|
||||
return Default()
|
||||
}
|
||||
|
||||
// Conflicts used by the router optimizer
|
||||
func (c *Cors) Conflicts() string {
|
||||
return "httpmethod"
|
||||
}
|
||||
|
||||
// Serve serves the middleware
|
||||
func (c *Cors) Serve(ctx *iris.Context) {
|
||||
if ctx.MethodString() == iris.MethodOptions {
|
||||
c.logf("Serve: Preflight request")
|
||||
c.handlePreflight(ctx)
|
||||
// Preflight requests are standalone and should stop the chain as some other
|
||||
// middleware may not handle OPTIONS requests correctly. One typical example
|
||||
// is authentication middleware ; OPTIONS requests won't carry authentication
|
||||
// headers (see #1)
|
||||
if c.optionPassthrough {
|
||||
ctx.Next()
|
||||
}
|
||||
} else {
|
||||
c.logf("Serve: Actual request")
|
||||
c.handleActualRequest(ctx)
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// handlePreflight handles pre-flight CORS requests
|
||||
func (c *Cors) handlePreflight(ctx *iris.Context) {
|
||||
origin := ctx.RequestHeader("Origin")
|
||||
|
||||
if ctx.MethodString() != iris.MethodOptions {
|
||||
c.logf(" Preflight aborted: %s!=OPTIONS", ctx.MethodString())
|
||||
return
|
||||
}
|
||||
// Always set Vary headers
|
||||
ctx.Response.Header.Add("Vary", "Origin")
|
||||
ctx.Response.Header.Add("Vary", "Access-Control-Request-Method")
|
||||
ctx.Response.Header.Add("Vary", "Access-Control-Request-Headers")
|
||||
|
||||
if origin == "" {
|
||||
c.logf(" Preflight aborted: empty origin")
|
||||
return
|
||||
}
|
||||
if !c.isOriginAllowed(origin) {
|
||||
c.logf(" Preflight aborted: origin '%s' not allowed", origin)
|
||||
return
|
||||
}
|
||||
|
||||
reqMethod := ctx.RequestHeader("Access-Control-Request-Method")
|
||||
if !c.isMethodAllowed(reqMethod) {
|
||||
c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
|
||||
return
|
||||
}
|
||||
reqHeaders := parseHeaderList(ctx.RequestHeader("Access-Control-Request-Headers"))
|
||||
if !c.areHeadersAllowed(reqHeaders) {
|
||||
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
|
||||
return
|
||||
}
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Origin", origin)
|
||||
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
|
||||
// by Access-Control-Request-Method (if supported) can be enough
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
|
||||
if len(reqHeaders) > 0 {
|
||||
|
||||
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
|
||||
// from Access-Control-Request-Headers can be enough
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
|
||||
}
|
||||
if c.allowCredentials {
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
if c.maxAge > 0 {
|
||||
ctx.Response.Header.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
|
||||
}
|
||||
c.logf(" Preflight response headers: %v", ctx.Response.Header)
|
||||
}
|
||||
|
||||
// handleActualRequest handles simple cross-origin requests, actual request or redirects
|
||||
func (c *Cors) handleActualRequest(ctx *iris.Context) {
|
||||
origin := ctx.RequestHeader("Origin")
|
||||
|
||||
if ctx.MethodString() == "OPTIONS" {
|
||||
c.logf(" Actual request no headers added: method == %s", ctx.MethodString())
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Response.Header.Add("Vary", "Origin")
|
||||
if origin == "" {
|
||||
c.logf(" Actual request no headers added: missing origin")
|
||||
return
|
||||
}
|
||||
if !c.isOriginAllowed(origin) {
|
||||
c.logf(" Actual request no headers added: origin '%s' not allowed", origin)
|
||||
return
|
||||
}
|
||||
|
||||
// Note that spec does define a way to specifically disallow a simple method like GET or
|
||||
// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
|
||||
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
|
||||
// We think it's a nice feature to be able to have control on those methods though.
|
||||
if !c.isMethodAllowed(ctx.MethodString()) {
|
||||
c.logf(" Actual request no headers added: method '%s' not allowed", ctx.MethodString())
|
||||
return
|
||||
}
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Origin", origin)
|
||||
if len(c.exposedHeaders) > 0 {
|
||||
ctx.Response.Header.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
|
||||
}
|
||||
if c.allowCredentials {
|
||||
ctx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
c.logf(" Actual response added headers: %v", ctx.Response.Header)
|
||||
}
|
||||
|
||||
// convenience method. checks if debugging is turned on before printing
|
||||
func (c *Cors) logf(format string, a ...interface{}) {
|
||||
if c.Log != nil {
|
||||
c.Log.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
|
||||
// on the endpoint
|
||||
func (c *Cors) isOriginAllowed(origin string) bool {
|
||||
if c.allowOriginFunc != nil {
|
||||
return c.allowOriginFunc(origin)
|
||||
}
|
||||
if c.allowedOriginsAll {
|
||||
return true
|
||||
}
|
||||
origin = strings.ToLower(origin)
|
||||
for _, o := range c.allowedOrigins {
|
||||
if o == origin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, w := range c.allowedWOrigins {
|
||||
if w.match(origin) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
|
||||
// on the endpoing
|
||||
func (c *Cors) isMethodAllowed(method string) bool {
|
||||
if len(c.allowedMethods) == 0 {
|
||||
// If no method allowed, always return false, even for preflight request
|
||||
return false
|
||||
}
|
||||
method = strings.ToUpper(method)
|
||||
if method == "OPTIONS" {
|
||||
// Always allow preflight requests
|
||||
return true
|
||||
}
|
||||
for _, m := range c.allowedMethods {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// areHeadersAllowed checks if a given list of headers are allowed to used within
|
||||
// a cross-domain request.
|
||||
func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
|
||||
if c.allowedHeadersAll || len(requestedHeaders) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, header := range requestedHeaders {
|
||||
header = http.CanonicalHeaderKey(header)
|
||||
found := false
|
||||
for _, h := range c.allowedHeaders {
|
||||
if h == header {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
## Middleware information
|
||||
|
||||
This folder contains a middleware for internationalization uses a third-party package named i81n.
|
||||
|
||||
More can be found here:
|
||||
[https://github.com/Unknwon/i18n](https://github.com/Unknwon/i18n)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Package i18n is for app Internationalization and Localization.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
Create folder named 'locales'
|
||||
```
|
||||
///Files:
|
||||
|
||||
./locales/locale_en-US.ini
|
||||
./locales/locale_el-US.ini
|
||||
```
|
||||
Contents on locale_en-US:
|
||||
```
|
||||
hi = hello, %s
|
||||
```
|
||||
Contents on locale_el-GR:
|
||||
```
|
||||
hi = Ãåéá, %s
|
||||
```
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/i18n"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
iris.UseFunc(i18n.I18n(i18n.Options{Default: "en-US",
|
||||
Languages: map[string]string{
|
||||
"en-US": "./locales/locale_en-US.ini",
|
||||
"el-GR": "./locales/locale_el-GR.ini",
|
||||
"zh-CN": "./locales/locale_zh-CN.ini"}}))
|
||||
// or iris.Use(i18n.I18nHandler(....))
|
||||
// or iris.Get("/",i18n.I18n(....), func (ctx *iris.Context){})
|
||||
|
||||
iris.Get("/", func(ctx *iris.Context) {
|
||||
hi := ctx.GetFmt("translate")("hi", "maki") // hi is the key, 'maki' is the %s, the second parameter is optional
|
||||
language := ctx.Get("language") // language is the language key, example 'en-US'
|
||||
|
||||
ctx.Write("From the language %s translated output: %s", language, hi)
|
||||
})
|
||||
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### [For a working example, click here](https://github.com/kataras/iris/tree/examples/middleware_internationalization_i18n)
|
|
@ -1,99 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/i18n"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
// AcceptLanguage is the Header key "Accept-Language"
|
||||
const AcceptLanguage = "Accept-Language"
|
||||
|
||||
// Options the i18n options
|
||||
type Options struct {
|
||||
// Default set it if you want a default language
|
||||
//
|
||||
// Checked: Configuration state, not at runtime
|
||||
Default string
|
||||
// URLParameter is the name of the url parameter which the language can be indentified
|
||||
//
|
||||
// Checked: Serving state, runtime
|
||||
URLParameter string
|
||||
// Languages is a map[string]string which the key is the language i81n and the value is the file location
|
||||
//
|
||||
// Example of key is: 'en-US'
|
||||
// Example of value is: './locales/en-US.ini'
|
||||
Languages map[string]string
|
||||
}
|
||||
|
||||
type i18nMiddleware struct {
|
||||
options Options
|
||||
}
|
||||
|
||||
func (i *i18nMiddleware) Serve(ctx *iris.Context) {
|
||||
wasByCookie := false
|
||||
// try to get by url parameter
|
||||
language := ctx.URLParam(i.options.URLParameter)
|
||||
|
||||
if language == "" {
|
||||
// then try to take the lang field from the cookie
|
||||
language = ctx.GetCookie("lang")
|
||||
|
||||
if len(language) > 0 {
|
||||
wasByCookie = true
|
||||
} else {
|
||||
// try to get by the request headers(?)
|
||||
if langHeader := ctx.RequestHeader(AcceptLanguage); i18n.IsExist(langHeader) {
|
||||
language = langHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it was not taken by the cookie, then set the cookie in order to have it
|
||||
if !wasByCookie {
|
||||
ctx.SetCookieKV("language", language)
|
||||
}
|
||||
if language == "" {
|
||||
language = i.options.Default
|
||||
}
|
||||
locale := i18n.Locale{language}
|
||||
ctx.Set("language", language)
|
||||
ctx.Set("translate", locale.Tr)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// I18nHandler returns the middleware which is just an iris.handler
|
||||
func I18nHandler(_options ...Options) *i18nMiddleware {
|
||||
i := &i18nMiddleware{}
|
||||
if len(_options) == 0 || (len(_options) > 0 && len(_options[0].Languages) == 0) {
|
||||
panic("You cannot use this middleware without set the Languages option, please try again and read the docs.")
|
||||
}
|
||||
|
||||
i.options = _options[0]
|
||||
firstlanguage := ""
|
||||
//load the files
|
||||
for k, v := range i.options.Languages {
|
||||
if !strings.HasSuffix(v, ".ini") {
|
||||
v += ".ini"
|
||||
}
|
||||
err := i18n.SetMessage(k, v)
|
||||
if err != nil && err != i18n.ErrLangAlreadyExist {
|
||||
panic("Iris i18n Middleware: Failed to set locale file" + k + " Error:" + err.Error())
|
||||
}
|
||||
if firstlanguage == "" {
|
||||
firstlanguage = k
|
||||
}
|
||||
}
|
||||
// if not default language setted then set to the first of the i.options.Languages
|
||||
if i.options.Default == "" {
|
||||
i.options.Default = firstlanguage
|
||||
}
|
||||
|
||||
i18n.SetDefaultLang(i.options.Default)
|
||||
return i
|
||||
}
|
||||
|
||||
// I18n returns the middleware as iris.HandlerFunc with the passed options
|
||||
func I18n(_options ...Options) iris.HandlerFunc {
|
||||
return I18nHandler(_options...).Serve
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
## Middleware information
|
||||
|
||||
This folder contains a middleware which is a bridge between Iris station's logger and http requests.
|
||||
|
||||
**Logs the incoming requests**
|
||||
|
||||
## How to use
|
||||
|
||||
Read the logger section [here](https://kataras.gitbooks.io/iris/content/logger.html)
|
|
@ -1,100 +0,0 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
)
|
||||
|
||||
// Options are the options of the logger middlweare
|
||||
// contains 5 bools
|
||||
// Status, IP, Method, Path, EnableColors
|
||||
// if set to true then these will print
|
||||
type Options struct {
|
||||
// Status displays status code (bool)
|
||||
Status bool
|
||||
// IP displays request's remote address (bool)
|
||||
IP bool
|
||||
// Method displays the http method (bool)
|
||||
Method bool
|
||||
// Path displays the request path (bool)
|
||||
Path bool
|
||||
// EnableColors defaults to false
|
||||
EnableColors bool
|
||||
}
|
||||
|
||||
// DefaultOptions returns an options which all properties are true
|
||||
func DefaultOptions() Options {
|
||||
return Options{true, true, true, true, false}
|
||||
}
|
||||
|
||||
type loggerMiddleware struct {
|
||||
*logger.Logger
|
||||
options Options
|
||||
}
|
||||
|
||||
// Serve serves the middleware
|
||||
func (l *loggerMiddleware) Serve(ctx *iris.Context) {
|
||||
//all except latency to string
|
||||
var date, status, ip, method, path string
|
||||
var latency time.Duration
|
||||
var startTime, endTime time.Time
|
||||
path = ctx.PathString()
|
||||
method = ctx.MethodString()
|
||||
|
||||
startTime = time.Now()
|
||||
|
||||
ctx.Next()
|
||||
//no time.Since in order to format it well after
|
||||
endTime = time.Now()
|
||||
date = endTime.Format("01/02 - 15:04:05")
|
||||
latency = endTime.Sub(startTime)
|
||||
|
||||
if l.options.Status {
|
||||
status = strconv.Itoa(ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
if l.options.IP {
|
||||
ip = ctx.RemoteAddr()
|
||||
}
|
||||
|
||||
if !l.options.Method {
|
||||
method = ""
|
||||
}
|
||||
|
||||
if !l.options.Path {
|
||||
path = ""
|
||||
}
|
||||
|
||||
//finally print the logs
|
||||
l.printf("%s %v %4v %s %s %s \n", date, status, latency, ip, method, path)
|
||||
|
||||
}
|
||||
|
||||
func (l *loggerMiddleware) printf(format string, a ...interface{}) {
|
||||
if l.options.EnableColors {
|
||||
l.Logger.Otherf(format, a...)
|
||||
} else {
|
||||
l.Logger.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns the logger middleware as HandlerFunc with the default settings if second parameter is not passed
|
||||
func New(theLogger *logger.Logger, options ...Options) iris.HandlerFunc {
|
||||
if theLogger == nil {
|
||||
theLogger = logger.New(config.DefaultLogger())
|
||||
}
|
||||
|
||||
l := &loggerMiddleware{Logger: theLogger}
|
||||
|
||||
if len(options) > 0 {
|
||||
l.options = options[0]
|
||||
} else {
|
||||
l.options = DefaultOptions()
|
||||
}
|
||||
|
||||
return l.Serve
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
## Middleware information
|
||||
|
||||
This folder contains a middleware for safety recover the server from panic
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/recovery"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
iris.Use(recovery.New(os.Stderr)) // optional parameter is the writer which the stack of the panic will be printed
|
||||
|
||||
iris.Get("/", func(ctx *iris.Context) {
|
||||
ctx.Write("Hi, let's panic")
|
||||
panic("errorrrrrrrrrrrrrrr")
|
||||
})
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
}
|
||||
|
||||
```
|
|
@ -1,45 +0,0 @@
|
|||
package recovery
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type recovery struct {
|
||||
//out optional output to log any panics
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (r recovery) Serve(ctx *iris.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
r.out.Write([]byte("[" + time.Now().String() + "]Recovery from panic \n"))
|
||||
//ctx.Panic just sends http status 500 by default, but you can change it by: iris.OnPanic(func( c *iris.Context){})
|
||||
ctx.Panic()
|
||||
}
|
||||
}()
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
// Recovery restores the server on internal server errors (panics)
|
||||
// receives an optional writer, the default is the os.Stderr if no out writer given
|
||||
// returns the middleware as iris.Handler
|
||||
// same as New(...)
|
||||
func Recovery(out ...io.Writer) iris.Handler {
|
||||
r := recovery{os.Stderr}
|
||||
if out != nil && len(out) == 1 {
|
||||
r.out = out[0]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// New restores the server on internal server errors (panics)
|
||||
// receives an optional writer, the default is the os.Stderr if no out writer given
|
||||
// returns the middleware as iris.Handler
|
||||
// same as Recovery(...)
|
||||
func New(out ...io.Writer) iris.Handler {
|
||||
return Recovery(out...)
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
## Middleware information
|
||||
|
||||
This was out-of-the-box iris supported before, but after Iris V1.1.0 it's not, so I had to modify it.
|
||||
|
||||
|
||||
This folder contains a middleware ported to Iris from a third-party middleware named secure.
|
||||
|
||||
More can be found here:
|
||||
[https://github.com/unrolled/secure](https://github.com/unrolled/secure)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Secure is an HTTP middleware for Go that facilitates some quick security wins.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/middleware/secure"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := secure.New(secure.Options{
|
||||
AllowedHosts: []string{"ssl.example.com"}, // AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
|
||||
SSLRedirect: true, // If SSLRedirect is set to true, then only allow HTTPS requests. Default is false.
|
||||
SSLTemporaryRedirect: false, // If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
|
||||
SSLHost: "ssl.example.com", // SSLHost is the host name that is used to redirect HTTP requests to HTTPS. Default is "", which indicates to use the same host.
|
||||
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"}, // SSLProxyHeaders is set of header keys with associated values that would indicate a valid HTTPS request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
|
||||
STSSeconds: 315360000, // STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
|
||||
STSIncludeSubdomains: true, // If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
|
||||
STSPreload: true, // If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
|
||||
ForceSTSHeader: false, // STS header is only included when the connection is HTTPS. If you want to force it to always be added, set to true. `IsDevelopment` still overrides this. Default is false.
|
||||
FrameDeny: true, // If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
|
||||
CustomFrameOptionsValue: "SAMEORIGIN", // CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
|
||||
ContentTypeNosniff: true, // If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
|
||||
BrowserXSSFilter: true, // If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
|
||||
ContentSecurityPolicy: "default-src 'self'", // ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
|
||||
PublicKey: `pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubdomains; report-uri="https://www.example.com/hpkp-report"`, // PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
|
||||
|
||||
IsDevelopment: true, // This will cause the AllowedHosts, SSLRedirect, and STSSeconds/STSIncludeSubdomains options to be ignored during development. When deploying to production, be sure to set this to false.
|
||||
})
|
||||
|
||||
iris.UseFunc(func(c *iris.Context) {
|
||||
err := s.Process(c)
|
||||
|
||||
// If there was an error, do not continue.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
})
|
||||
|
||||
iris.Get("/home", func(c *iris.Context) {
|
||||
c.Write("Hello from /home")
|
||||
})
|
||||
|
||||
println("Server is running at :8080")
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
This has been modified to work with Iris, credits goes to https://github.com/unrolled/secure
|
||||
*/
|
||||
|
||||
package secure
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
const (
|
||||
stsHeader = "Strict-Transport-Security"
|
||||
stsSubdomainString = "; includeSubdomains"
|
||||
stsPreloadString = "; preload"
|
||||
frameOptionsHeader = "X-Frame-Options"
|
||||
frameOptionsValue = "DENY"
|
||||
contentTypeHeader = "X-Content-Type-Options"
|
||||
contentTypeValue = "nosniff"
|
||||
xssProtectionHeader = "X-XSS-Protection"
|
||||
xssProtectionValue = "1; mode=block"
|
||||
cspHeader = "Content-Security-Policy"
|
||||
hpkpHeader = "Public-Key-Pins"
|
||||
)
|
||||
|
||||
func defaultBadHostHandler(ctx *iris.Context) {
|
||||
ctx.Text(iris.StatusInternalServerError, "Bad Host")
|
||||
}
|
||||
|
||||
// Options is a struct for specifying configuration options for the secure.Secure middleware.
|
||||
type Options struct {
|
||||
// AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
|
||||
AllowedHosts []string
|
||||
// If SSLRedirect is set to true, then only allow https requests. Default is false.
|
||||
SSLRedirect bool
|
||||
// If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
|
||||
SSLTemporaryRedirect bool
|
||||
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
|
||||
SSLHost string
|
||||
// SSLProxyHeaders is set of header keys with associated values that would indicate a valid https request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
|
||||
SSLProxyHeaders map[string]string
|
||||
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
|
||||
STSSeconds int64
|
||||
// If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
|
||||
STSIncludeSubdomains bool
|
||||
// If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
|
||||
STSPreload bool
|
||||
// If ForceSTSHeader is set to true, the STS header will be added even when the connection is HTTP. Default is false.
|
||||
ForceSTSHeader bool
|
||||
// If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
|
||||
FrameDeny bool
|
||||
// CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
|
||||
CustomFrameOptionsValue string
|
||||
// If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
|
||||
ContentTypeNosniff bool
|
||||
// BrowserXSSFilter If it's true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
|
||||
BrowserXSSFilter bool
|
||||
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
|
||||
ContentSecurityPolicy string
|
||||
// PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
|
||||
PublicKey string
|
||||
// When developing, the AllowedHosts, SSL, and STS options can cause some unwanted effects. Usually testing happens on http, not https, and on localhost, not your production domain... so set this to true for dev environment.
|
||||
// If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as false. Default if false.
|
||||
IsDevelopment bool
|
||||
}
|
||||
|
||||
// Secure is a middleware that helps setup a few basic security features. A single secure.Options struct can be
|
||||
// provided to configure which features should be enabled, and the ability to override a few of the default values.
|
||||
type Secure struct {
|
||||
// Customize Secure with an Options struct.
|
||||
opt Options
|
||||
|
||||
// Handlers for when an error occurs (ie bad host).
|
||||
badHostHandler iris.Handler
|
||||
}
|
||||
|
||||
// New constructs a new Secure instance with supplied options.
|
||||
func New(options ...Options) *Secure {
|
||||
var o Options
|
||||
if len(options) == 0 {
|
||||
o = Options{}
|
||||
} else {
|
||||
o = options[0]
|
||||
}
|
||||
|
||||
return &Secure{
|
||||
opt: o,
|
||||
badHostHandler: iris.HandlerFunc(defaultBadHostHandler),
|
||||
}
|
||||
}
|
||||
|
||||
// SetBadHostHandler sets the handler to call when secure rejects the host name.
|
||||
func (s *Secure) SetBadHostHandler(handler iris.Handler) {
|
||||
s.badHostHandler = handler
|
||||
}
|
||||
|
||||
// Handler implements the iris.HandlerFunc for integration with iris.
|
||||
func (s *Secure) Handler(h iris.Handler) iris.Handler {
|
||||
return iris.HandlerFunc(func(ctx *iris.Context) {
|
||||
// Let secure process the request. If it returns an error,
|
||||
// that indicates the request should not continue.
|
||||
err := s.Process(ctx)
|
||||
|
||||
// If there was an error, do not continue.
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h.Serve(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// Process runs the actual checks and returns an error if the middleware chain should stop.
|
||||
func (s *Secure) Process(ctx *iris.Context) error {
|
||||
// Allowed hosts check.
|
||||
if len(s.opt.AllowedHosts) > 0 && !s.opt.IsDevelopment {
|
||||
isGoodHost := false
|
||||
for _, allowedHost := range s.opt.AllowedHosts {
|
||||
if strings.EqualFold(allowedHost, string(ctx.Host())) {
|
||||
isGoodHost = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isGoodHost {
|
||||
s.badHostHandler.Serve(ctx)
|
||||
return fmt.Errorf("Bad host name: %s", string(ctx.Host()))
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we are on HTTPS.
|
||||
isSSL := strings.EqualFold(string(ctx.Request.URI().Scheme()), "https") || ctx.IsTLS()
|
||||
if !isSSL {
|
||||
for k, v := range s.opt.SSLProxyHeaders {
|
||||
if ctx.RequestHeader(k) == v {
|
||||
isSSL = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SSL check.
|
||||
if s.opt.SSLRedirect && !isSSL && !s.opt.IsDevelopment {
|
||||
url := ctx.Request.URI()
|
||||
url.SetScheme("https")
|
||||
url.SetHostBytes(ctx.Host())
|
||||
|
||||
if len(s.opt.SSLHost) > 0 {
|
||||
url.SetHost(s.opt.SSLHost)
|
||||
}
|
||||
|
||||
status := iris.StatusMovedPermanently
|
||||
if s.opt.SSLTemporaryRedirect {
|
||||
status = iris.StatusTemporaryRedirect
|
||||
}
|
||||
|
||||
ctx.Redirect(url.String(), status)
|
||||
return fmt.Errorf("Redirecting to HTTPS")
|
||||
}
|
||||
|
||||
// Strict Transport Security header. Only add header when we know it's an SSL connection.
|
||||
// See https://tools.ietf.org/html/rfc6797#section-7.2 for details.
|
||||
if s.opt.STSSeconds != 0 && (isSSL || s.opt.ForceSTSHeader) && !s.opt.IsDevelopment {
|
||||
stsSub := ""
|
||||
if s.opt.STSIncludeSubdomains {
|
||||
stsSub = stsSubdomainString
|
||||
}
|
||||
|
||||
if s.opt.STSPreload {
|
||||
stsSub += stsPreloadString
|
||||
}
|
||||
|
||||
ctx.Response.Header.Add(stsHeader, fmt.Sprintf("max-age=%d%s", s.opt.STSSeconds, stsSub))
|
||||
}
|
||||
|
||||
// Frame Options header.
|
||||
if len(s.opt.CustomFrameOptionsValue) > 0 {
|
||||
ctx.Response.Header.Add(frameOptionsHeader, s.opt.CustomFrameOptionsValue)
|
||||
} else if s.opt.FrameDeny {
|
||||
ctx.Response.Header.Add(frameOptionsHeader, frameOptionsValue)
|
||||
}
|
||||
|
||||
// Content Type Options header.
|
||||
if s.opt.ContentTypeNosniff {
|
||||
ctx.Response.Header.Add(contentTypeHeader, contentTypeValue)
|
||||
}
|
||||
|
||||
// XSS Protection header.
|
||||
if s.opt.BrowserXSSFilter {
|
||||
ctx.Response.Header.Add(xssProtectionHeader, xssProtectionValue)
|
||||
}
|
||||
|
||||
// HPKP header.
|
||||
if len(s.opt.PublicKey) > 0 && isSSL && !s.opt.IsDevelopment {
|
||||
ctx.Response.Header.Add(hpkpHeader, s.opt.PublicKey)
|
||||
}
|
||||
|
||||
// Content Security Policy header.
|
||||
if len(s.opt.ContentSecurityPolicy) > 0 {
|
||||
ctx.Response.Header.Add(cspHeader, s.opt.ContentSecurityPolicy)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -3,7 +3,7 @@ package iris
|
|||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/iris-contrib/errors"
|
||||
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/utils"
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
## Package information
|
||||
|
||||
Editor Plugin is just a bridge between Iris and [alm-tools](http://alm.tools).
|
||||
|
||||
|
||||
[alm-tools](http://alm.tools) is a typescript online IDE/Editor, made by [@basarat](https://twitter.com/basarat) one of the top contributors of the [Typescript](http://www.typescriptlang.org).
|
||||
|
||||
Iris gives you the opportunity to edit your client-side using the alm-tools editor, via the editor plugin.
|
||||
|
||||
|
||||
This plugin starts it's own server, if Iris server is using TLS then the editor will use the same key and cert.
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/plugin/editor"
|
||||
)
|
||||
|
||||
func main(){
|
||||
editorConfig:= config.Editor {
|
||||
Host: "127.0.0.1",
|
||||
Port: 4444,
|
||||
WorkingDir: "/path/to/the/client/side/directory",
|
||||
Username: "myusername",
|
||||
Password: "mypassword"
|
||||
|
||||
}
|
||||
|
||||
iris.Plugins.Add(editor.New(editorConfig))
|
||||
|
||||
iris.Get("/", func (ctx *iris.Context){})
|
||||
|
||||
iris.Listen(":8080")
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
> Note for username, password: The Authorization specifies the authentication mechanism (in this case Basic) followed by the username and password.
|
||||
Although, the string aHR0cHdhdGNoOmY= may look encrypted it is simply a base64 encoded version of <username>:<password>.
|
||||
Would be readily available to anyone who could intercept the HTTP request. [Read more.](https://www.httpwatch.com/httpgallery/authentication/)
|
||||
|
||||
> The editor can't work if the directory doesn't contains a [tsconfig.json](http://www.typescriptlang.org/docs/handbook/tsconfig.json.html).
|
||||
|
||||
> If you are using the [typescript plugin](https://github.com/kataras/iris/tree/development/plugin/typescript) you don't have to call the .Dir(...)
|
||||
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
package editor
|
||||
|
||||
/* Notes for Auth
|
||||
The Authorization specifies the authentication mechanism (in this case Basic) followed by the username and password.
|
||||
Although, the string aHR0cHdhdGNoOmY= may look encrypted it is simply a base64 encoded version of <username>:<password>.
|
||||
Would be readily available to anyone who could intercept the HTTP request.
|
||||
*/
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/iris-contrib/npm"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// Name the name of the Plugin, which is "EditorPlugin"
|
||||
Name = "EditorPlugin"
|
||||
)
|
||||
|
||||
type (
|
||||
// Plugin is an Editor Plugin the struct which implements the iris.IPlugin
|
||||
// it holds a logger from the iris' station
|
||||
// username,password for basic auth
|
||||
// directory which the client side code is
|
||||
// keyfile,certfile for TLS listening
|
||||
// and a host which is listening for
|
||||
Plugin struct {
|
||||
config *config.Editor
|
||||
logger *logger.Logger
|
||||
enabled bool // default true
|
||||
keyfile string
|
||||
certfile string
|
||||
// after alm started
|
||||
process *os.Process
|
||||
}
|
||||
)
|
||||
|
||||
// New creates and returns an Editor Plugin instance
|
||||
func New(cfg ...config.Editor) *Plugin {
|
||||
c := config.DefaultEditor().Merge(cfg)
|
||||
e := &Plugin{enabled: true, config: &c}
|
||||
return e
|
||||
}
|
||||
|
||||
// User set a user, accepts two parameters: username (string), string (string)
|
||||
func (e *Plugin) User(username string, password string) *Plugin {
|
||||
e.config.Username = username
|
||||
e.config.Password = password
|
||||
return e
|
||||
}
|
||||
|
||||
// Dir sets the directory which the client side source code alive
|
||||
func (e *Plugin) Dir(workingDir string) *Plugin {
|
||||
e.config.WorkingDir = workingDir
|
||||
return e
|
||||
}
|
||||
|
||||
// Port sets the port (int) for the editor plugin's standalone server
|
||||
func (e *Plugin) Port(port int) *Plugin {
|
||||
e.config.Port = port
|
||||
return e
|
||||
}
|
||||
|
||||
// SetEnable if true enables the editor plugin, otherwise disables it
|
||||
func (e *Plugin) SetEnable(enable bool) {
|
||||
e.enabled = enable
|
||||
}
|
||||
|
||||
// GetName returns the name of the Plugin
|
||||
func (e *Plugin) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// GetDescription EditorPlugin is a bridge between Iris and the alm-tools, the browser-based IDE for client-side sources.
|
||||
func (e *Plugin) GetDescription() string {
|
||||
return Name + " is a bridge between Iris and the alm-tools, the browser-based IDE for client-side sources. \n"
|
||||
}
|
||||
|
||||
// PreListen runs before the server's listens, saves the keyfile,certfile and the host from the Iris station to listen for
|
||||
func (e *Plugin) PreListen(s *iris.Framework) {
|
||||
e.logger = s.Logger
|
||||
e.keyfile = s.Config.Server.KeyFile
|
||||
e.certfile = s.Config.Server.CertFile
|
||||
|
||||
if e.config.Host == "" {
|
||||
e.config.Host = s.HTTPServer.VirtualHostname()
|
||||
}
|
||||
e.start()
|
||||
}
|
||||
|
||||
// PreClose kills the editor's server when Iris is closed
|
||||
func (e *Plugin) PreClose(s *iris.Framework) {
|
||||
if e.process != nil {
|
||||
err := e.process.Kill()
|
||||
if err != nil {
|
||||
e.logger.Printf("\nError while trying to terminate the (Editor)Plugin, please kill this process by yourself, process id: %d", e.process.Pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// start starts the job
|
||||
func (e *Plugin) start() {
|
||||
if e.config.Username == "" || e.config.Password == "" {
|
||||
e.logger.Println("Error before running alm-tools. You have to set username & password for security reasons, otherwise this plugin won't run.")
|
||||
return
|
||||
}
|
||||
|
||||
if !npm.Exists("alm/bin/alm") {
|
||||
e.logger.Println("Installing alm-tools, please wait...")
|
||||
res := npm.Install("alm")
|
||||
if res.Error != nil {
|
||||
e.logger.Print(res.Error.Error())
|
||||
return
|
||||
}
|
||||
e.logger.Print(res.Message)
|
||||
}
|
||||
|
||||
cmd := utils.CommandBuilder("node", npm.Abs("alm/src/server.js"))
|
||||
cmd.AppendArguments("-a", e.config.Username+":"+e.config.Password, "-h", e.config.Host, "-t", strconv.Itoa(e.config.Port), "-d", e.config.WorkingDir[0:len(e.config.WorkingDir)-1])
|
||||
// for auto-start in the browser: cmd.AppendArguments("-o")
|
||||
if e.keyfile != "" && e.certfile != "" {
|
||||
cmd.AppendArguments("--httpskey", e.keyfile, "--httpscert", e.certfile)
|
||||
}
|
||||
|
||||
//For debug only:
|
||||
//cmd.Stdout = os.Stdout
|
||||
//cmd.Stderr = os.Stderr
|
||||
//os.Stdin = os.Stdin
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
e.logger.Println("Error while running alm-tools. Trace: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//we lose the internal error handling but ok...
|
||||
e.logger.Printf("Editor is running at %s:%d | %s", e.config.Host, e.config.Port, e.config.WorkingDir)
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
## Package information
|
||||
|
||||
Iris control is a plugin which gives you a little control over your Iris station.
|
||||
|
||||
|
||||
```go
|
||||
iris.Plugins.Add(iriscontrol.New(PORT, AUTHENTICATED_USERS))
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/plugin/iriscontrol"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
iris.Plugins.Add(iriscontrol.New(9090, map[string]string{
|
||||
"1": "1",
|
||||
"irisusername2": "irispassowrd2",
|
||||
}))
|
||||
|
||||
iris.Get("/", func(ctx *iris.Context) {
|
||||
ctx.Write("Root path from server")
|
||||
})
|
||||
|
||||
iris.Get("/something", func(ctx *iris.Context) {
|
||||
ctx.Write("Something path from server")
|
||||
})
|
||||
|
||||
// Iris control will listen on mydomain.com:9090
|
||||
iris.Listen("mydomain.com:8080")
|
||||
}
|
||||
```
|
||||
|
||||
[![Iris control show case](https://raw.githubusercontent.com/iris-contrib/website/gh-pages/assets/iriscontrolplugin.gif)](https://github.com/iris-contrib/examples/tree/master/plugin_iriscontrol)
|
|
@ -1,211 +0,0 @@
|
|||
package iriscontrol
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/middleware/basicauth"
|
||||
"github.com/kataras/iris/websocket"
|
||||
)
|
||||
|
||||
type (
|
||||
// IrisControl is the interface which the iriscontrol should implements
|
||||
// it's empty for now because no need any public API
|
||||
IrisControl interface{}
|
||||
iriscontrol struct {
|
||||
port int
|
||||
users map[string]string
|
||||
|
||||
// child is the plugin's standalone station
|
||||
child *iris.Framework
|
||||
// the station which this plugins is registed to
|
||||
parent *iris.Framework
|
||||
parentLastOp time.Time
|
||||
|
||||
// websocket
|
||||
clients clients
|
||||
}
|
||||
|
||||
clients []websocket.Connection
|
||||
|
||||
pluginInfo struct {
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
logInfo struct {
|
||||
Date string
|
||||
Status int
|
||||
Latency time.Duration
|
||||
IP string
|
||||
Method string
|
||||
Subdomain string
|
||||
Path string
|
||||
}
|
||||
)
|
||||
|
||||
func (c clients) indexOf(connectionID string) int {
|
||||
for i := range c {
|
||||
if c[i].ID() == connectionID {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
var _ IrisControl = &iriscontrol{}
|
||||
|
||||
func (i *iriscontrol) listen(f *iris.Framework) {
|
||||
// set the path logger to the parent which will send the log via websocket to the browser
|
||||
f.MustUseFunc(func(ctx *iris.Context) {
|
||||
status := ctx.Response.StatusCode()
|
||||
path := ctx.PathString()
|
||||
method := ctx.MethodString()
|
||||
subdomain := ctx.Subdomain()
|
||||
ip := ctx.RemoteAddr()
|
||||
startTime := time.Now()
|
||||
|
||||
ctx.Next()
|
||||
//no time.Since in order to format it well after
|
||||
endTime := time.Now()
|
||||
date := endTime.Format("01/02 - 15:04:05")
|
||||
latency := endTime.Sub(startTime)
|
||||
info := logInfo{
|
||||
Date: date,
|
||||
Status: status,
|
||||
Latency: latency,
|
||||
IP: ip,
|
||||
Method: method,
|
||||
Subdomain: subdomain,
|
||||
Path: path,
|
||||
}
|
||||
i.Emit("log", info) //send this text to the browser,
|
||||
})
|
||||
|
||||
i.parent = f
|
||||
i.parentLastOp = time.Now()
|
||||
|
||||
i.initializeChild()
|
||||
}
|
||||
|
||||
func (i *iriscontrol) initializeChild() {
|
||||
i.child = iris.New()
|
||||
i.child.Config.DisableBanner = true
|
||||
i.child.Config.Render.Template.Directory = assetsPath + "templates"
|
||||
i.child.Config.Websocket.Endpoint = "/ws"
|
||||
|
||||
// set the assets
|
||||
i.child.Static("/public", assetsPath+"static", 1)
|
||||
|
||||
// set the authentication middleware to all except websocket
|
||||
auth := basicauth.New(config.BasicAuth{
|
||||
Users: i.users,
|
||||
ContextKey: "user",
|
||||
Realm: config.DefaultBasicAuthRealm,
|
||||
Expires: time.Duration(1) * time.Hour,
|
||||
})
|
||||
|
||||
i.child.UseFunc(func(ctx *iris.Context) {
|
||||
///TODO: Remove this and make client-side basic auth when websocket connection. (user@password/host.. on chronium)
|
||||
// FOR GOOGLE CHROME/CHRONIUM
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=123862
|
||||
// CROSS DOMAIN IS DISABLED so I think this is ok solution for now...
|
||||
if ctx.PathString() == i.child.Config.Websocket.Endpoint {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
auth.Serve(ctx)
|
||||
})
|
||||
|
||||
i.child.Websocket.OnConnection(func(c websocket.Connection) {
|
||||
// add the client to the list
|
||||
i.clients = append(i.clients, c)
|
||||
c.OnDisconnect(func() {
|
||||
// remove the client from the list
|
||||
if idx := i.clients.indexOf(c.ID()); idx != -1 {
|
||||
i.clients[idx] = i.clients[len(i.clients)-1]
|
||||
i.clients = i.clients[:len(i.clients)-1]
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
i.child.Get("/", func(ctx *iris.Context) {
|
||||
ctx.MustRender("index.html", iris.Map{
|
||||
"ServerIsRunning": i.parentIsRunning(),
|
||||
"Host": i.child.Config.Server.ListeningAddr,
|
||||
"Routes": i.parentLookups(),
|
||||
"Plugins": i.infoPlugins(),
|
||||
"LastOperationDateStr": i.infoLastOp(),
|
||||
})
|
||||
})
|
||||
|
||||
i.child.Post("/start_server", func(ctx *iris.Context) {
|
||||
|
||||
if !i.parentIsRunning() {
|
||||
// starts the server with its old configuration
|
||||
go func() {
|
||||
if err := i.parent.HTTPServer.Open(); err != nil {
|
||||
i.parent.Logger.Warningf(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
i.parentLastOp = time.Now()
|
||||
}
|
||||
})
|
||||
|
||||
i.child.Post("/stop_server", func(ctx *iris.Context) {
|
||||
|
||||
if i.parentIsRunning() {
|
||||
i.parentLastOp = time.Now()
|
||||
|
||||
go func() {
|
||||
if err := i.parent.CloseWithErr(); err != nil {
|
||||
i.parent.Logger.Warningf(err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
})
|
||||
|
||||
go i.child.Listen(i.parent.HTTPServer.VirtualHostname() + ":" + strconv.Itoa(i.port))
|
||||
}
|
||||
|
||||
func (i *iriscontrol) parentIsRunning() bool {
|
||||
return i.parent != nil && i.parent.HTTPServer.IsListening()
|
||||
}
|
||||
|
||||
func (i *iriscontrol) parentLookups() []iris.Route {
|
||||
if i.parent == nil {
|
||||
return nil
|
||||
}
|
||||
return i.parent.Lookups()
|
||||
}
|
||||
|
||||
func (i *iriscontrol) infoPlugins() (info []pluginInfo) {
|
||||
plugins := i.parent.Plugins
|
||||
for _, p := range plugins.GetAll() {
|
||||
name := plugins.GetName(p)
|
||||
description := plugins.GetDescription(p)
|
||||
if name == "" {
|
||||
name = "Unknown plugin name"
|
||||
}
|
||||
if description == "" {
|
||||
description = "description is not available"
|
||||
}
|
||||
|
||||
info = append(info, pluginInfo{Name: name, Description: description})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *iriscontrol) infoLastOp() string {
|
||||
return i.parentLastOp.Format(config.TimeFormat)
|
||||
}
|
||||
|
||||
func (i *iriscontrol) Emit(event string, msg interface{}) {
|
||||
for j := range i.clients {
|
||||
i.clients[j].Emit(event, msg)
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package iriscontrol
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
// Name "Iris Control"
|
||||
const Name = "Iris Control"
|
||||
|
||||
var (
|
||||
assetsURL = "https://github.com/iris-contrib/iris-control-assets/archive/master.zip"
|
||||
assetsUnzipname = "iris-control-assets"
|
||||
assetsPath = ""
|
||||
workingDir = ""
|
||||
)
|
||||
|
||||
// init just sets the assetsPath & current workingDir
|
||||
func init() {
|
||||
workingDir, _ = os.Getwd()
|
||||
assetsPath = utils.AssetsDirectory + utils.PathSeparator + "iris-control-assets" + utils.PathSeparator
|
||||
}
|
||||
|
||||
func installAssets() {
|
||||
|
||||
if !utils.DirectoryExists(assetsPath) {
|
||||
errMsg := "\nProblem while downloading the assets from the internet for the first time. Trace: %s"
|
||||
|
||||
installedDir, err := utils.Install(assetsURL, assetsPath)
|
||||
if err != nil {
|
||||
panic(errMsg)
|
||||
}
|
||||
|
||||
err = utils.CopyDir(installedDir, assetsPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// try to remove the unzipped folder
|
||||
utils.RemoveFile(installedDir[0 : len(installedDir)-1])
|
||||
}
|
||||
}
|
||||
|
||||
// New creates & returns a new iris control plugin
|
||||
// receives two parameters
|
||||
// first is the authenticated users which should be able to access the control panel
|
||||
// second is the PORT which the iris control panel should be listened & served to
|
||||
func New(port int, users map[string]string) IrisControl {
|
||||
return &iriscontrol{port: port, users: users}
|
||||
}
|
||||
|
||||
// PreListen registers the iriscontrol plugin
|
||||
func (i *iriscontrol) PreListen(s *iris.Framework) {
|
||||
installAssets()
|
||||
i.listen(s)
|
||||
}
|
||||
|
||||
// GetName returns the name of the plugin
|
||||
func (i *iriscontrol) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// GetDescription returns the description of the plugin
|
||||
func (i *iriscontrol) GetDescription() string {
|
||||
return Name + " is just a web interface which gives you control of your Iris.\n"
|
||||
}
|
||||
|
||||
// PreClose any clean-up
|
||||
// temporary is empty because all resources are cleaned graceful by the iris' station
|
||||
func (i *iriscontrol) PreClose(s *iris.Framework) {}
|
|
@ -1,83 +0,0 @@
|
|||
## Package information
|
||||
|
||||
This is an Iris and typescript bridge plugin.
|
||||
|
||||
1. Search for typescript files (.ts)
|
||||
2. Search for typescript projects (.tsconfig)
|
||||
3. If 1 || 2 continue else stop
|
||||
4. Check if typescript is installed, if not then auto-install it (always inside npm global modules, -g)
|
||||
5. If typescript project then build the project using tsc -p $dir
|
||||
6. If typescript files and no project then build each typescript using tsc $filename
|
||||
7. Watch typescript files if any changes happens, then re-build (5|6)
|
||||
|
||||
> Note: Ignore all typescript files & projects whose path has '/node_modules/'
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
This plugin has **optionally** options
|
||||
1. Bin: string, the typescript installation path/bin/tsc or tsc.cmd, if empty then it will search to the global npm modules
|
||||
2. Dir: string, Dir set the root, where to search for typescript files/project. Default "./"
|
||||
3. Ignore: string, comma separated ignore typescript files/project from these directories. Default "" (node_modules are always ignored)
|
||||
4. Tsconfig: &typescript.Tsconfig{}, here you can set all compilerOptions if no tsconfig.json exists inside the 'Dir'
|
||||
5. Editor: typescript.Editor(), if setted then alm-tools browser-based typescript IDE will be available. Defailt is nil
|
||||
|
||||
> Note: if any string in Ignore doesn't start with './' then it will ignore all files which contains this path string.
|
||||
For example /node_modules/ will ignore all typescript files that are inside at ANY '/node_modules/', that means and the submodules.
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/plugin/typescript"
|
||||
)
|
||||
|
||||
func main(){
|
||||
/* Options
|
||||
Bin -> the typescript installation path/bin/tsc or tsc.cmd, if empty then it will search to the global npm modules
|
||||
Dir -> where to search for typescript files/project. Default "./"
|
||||
Ignore -> comma separated ignore typescript files/project from these directories (/node_modules/ are always ignored). Default ""
|
||||
Tsconfig -> &typescript.Tsconfig{}, here you can set all compilerOptions if no tsconfig.json exists inside the 'Dir'
|
||||
Editor -> typescript.Editor(), if setted then alm-tools browser-based typescript IDE will be available. Default is nil.
|
||||
*/
|
||||
|
||||
ts := typescript.Options {
|
||||
Dir: "./scripts/src",
|
||||
Tsconfig: &typescript.Tsconfig{Module: "commonjs", Target: "es5"}, // or typescript.DefaultTsconfig()
|
||||
}
|
||||
|
||||
//if you want to change only certain option(s) but you want default to all others then you have to do this:
|
||||
ts = typescript.DefaultOptions()
|
||||
//
|
||||
|
||||
iris.Plugins.Add(typescript.New(ts)) //or with the default options just: typescript.New()
|
||||
|
||||
iris.Get("/", func (ctx *iris.Context){})
|
||||
|
||||
iris.Listen(":8080")
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Editor
|
||||
|
||||
[alm-tools](http://alm.tools) is a typescript online IDE/Editor, made by [@basarat](https://twitter.com/basarat) one of the top contributors of the [Typescript](http://www.typescriptlang.org).
|
||||
|
||||
Iris gives you the opportunity to edit your client-side using the alm-tools editor, via the editor plugin.
|
||||
With typescript plugin you have to set the Editor option and you're ready:
|
||||
|
||||
```go
|
||||
typescript.Options {
|
||||
//...
|
||||
Editor: typescript.Editor("username","passowrd")
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
> [Read more](https://github.com/kataras/iris/tree/development/plugin/editor) for Editor
|
|
@ -1,102 +0,0 @@
|
|||
package typescript
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type (
|
||||
// Tsconfig the struct for tsconfig.json
|
||||
Tsconfig struct {
|
||||
CompilerOptions CompilerOptions `json:"compilerOptions"`
|
||||
Exclude []string `json:"exclude"`
|
||||
}
|
||||
|
||||
// CompilerOptions contains all the compiler options used by the tsc (typescript compiler)
|
||||
CompilerOptions struct {
|
||||
Declaration bool `json:"declaration"`
|
||||
Module string `json:"module"`
|
||||
Target string `json:"target"`
|
||||
Watch bool `json:"watch"`
|
||||
Charset string `json:"charset"`
|
||||
Diagnostics bool `json:"diagnostics"`
|
||||
EmitBOM bool `json:"emitBOM"`
|
||||
EmitDecoratorMetadata bool `json:"emitDecoratorMetadata"`
|
||||
ExperimentalDecorators bool `json:"experimentalDecorators"`
|
||||
InlineSourceMap bool `json:"inlineSourceMap"`
|
||||
InlineSources bool `json:"inlineSources"`
|
||||
IsolatedModules bool `json:"isolatedModules"`
|
||||
Jsx string `json:"jsx"`
|
||||
ReactNamespace string `json:"reactNamespace"`
|
||||
ListFiles bool `json:"listFiles"`
|
||||
Locale string `json:"locale"`
|
||||
MapRoot string `json:"mapRoot"`
|
||||
ModuleResolution string `json:"moduleResolution"`
|
||||
NewLine string `json:"newLine"`
|
||||
NoEmit bool `json:"noEmit"`
|
||||
NoEmitOnError bool `json:"noEmitOnError"`
|
||||
NoEmitHelpers bool `json:"noEmitHelpers"`
|
||||
NoImplicitAny bool `json:"noImplicitAny"`
|
||||
NoLib bool `json:"noLib"`
|
||||
NoResolve bool `json:"noResolve"`
|
||||
SkipDefaultLibCheck bool `json:"skipDefaultLibCheck"`
|
||||
OutDir string `json:"outDir"`
|
||||
OutFile string `json:"outFile"`
|
||||
PreserveConstEnums bool `json:"preserveConstEnums"`
|
||||
Pretty bool `json:"pretty"`
|
||||
RemoveComments bool `json:"removeComments"`
|
||||
RootDir string `json:"rootDir"`
|
||||
SourceMap bool `json:"sourceMap"`
|
||||
SourceRoot string `json:"sourceRoot"`
|
||||
StripInternal bool `json:"stripInternal"`
|
||||
SuppressExcessPropertyErrors bool `json:"suppressExcessPropertyErrors"`
|
||||
SuppressImplicitAnyIndexErrors bool `json:"suppressImplicitAnyIndexErrors"`
|
||||
AllowUnusedLabels bool `json:"allowUnusedLabels"`
|
||||
NoImplicitReturns bool `json:"noImplicitReturns"`
|
||||
NoFallthroughCasesInSwitch bool `json:"noFallthroughCasesInSwitch"`
|
||||
AllowUnreachableCode bool `json:"allowUnreachableCode"`
|
||||
ForceConsistentCasingInFileNames bool `json:"forceConsistentCasingInFileNames"`
|
||||
AllowSyntheticDefaultImports bool `json:"allowSyntheticDefaultImports"`
|
||||
AllowJs bool `json:"allowJs"`
|
||||
NoImplicitUseStrict bool `json:"noImplicitUseStrict"`
|
||||
}
|
||||
)
|
||||
|
||||
// CompilerArgs returns the CompilerOptions' contents of the Tsconfig
|
||||
// it reads the json tags, add '--' at the start of each one and returns an array of strings
|
||||
func (tsconfig *Tsconfig) CompilerArgs() []string {
|
||||
val := reflect.ValueOf(tsconfig).Elem().FieldByName("CompilerOptions")
|
||||
compilerOpts := make([]string, val.NumField())
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
typeField := val.Type().Field(i)
|
||||
compilerOpts[i] = "--" + typeField.Tag.Get("json")
|
||||
}
|
||||
|
||||
return compilerOpts
|
||||
}
|
||||
|
||||
// FromFile reads a file & returns the Tsconfig by its contents
|
||||
func FromFile(tsConfigAbsPath string) *Tsconfig {
|
||||
file, err := ioutil.ReadFile(tsConfigAbsPath)
|
||||
if err != nil {
|
||||
panic("[IRIS TypescriptPlugin.FromFile]" + err.Error())
|
||||
}
|
||||
config := &Tsconfig{}
|
||||
json.Unmarshal(file, config)
|
||||
return config
|
||||
}
|
||||
|
||||
// DefaultTsconfig returns the default Tsconfig, with CompilerOptions module: commonjs, target: es5 and ignore the node_modules
|
||||
func DefaultTsconfig() *Tsconfig {
|
||||
return &Tsconfig{
|
||||
CompilerOptions: CompilerOptions{
|
||||
Module: "commonjs",
|
||||
Target: "es5",
|
||||
NoImplicitAny: false,
|
||||
SourceMap: false,
|
||||
},
|
||||
Exclude: []string{"node_modules"},
|
||||
}
|
||||
|
||||
}
|
|
@ -1,300 +0,0 @@
|
|||
package typescript
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/iris-contrib/npm"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/plugin/editor"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
/* Notes
|
||||
|
||||
The editor is working when the typescript plugin finds a typescript project (tsconfig.json),
|
||||
also working only if one typescript project found (normaly is one for client-side).
|
||||
|
||||
*/
|
||||
|
||||
// Name the name of the plugin, is "TypescriptPlugin"
|
||||
const Name = "TypescriptPlugin"
|
||||
|
||||
var nodeModules = utils.PathSeparator + "node_modules" + utils.PathSeparator
|
||||
|
||||
type (
|
||||
// Options the struct which holds the TypescriptPlugin options
|
||||
// Has five (5) fields
|
||||
//
|
||||
// 1. Bin: string, the typescript installation directory/typescript/lib/tsc.js, if empty it will search inside global npm modules
|
||||
// 2. Dir: string, Dir set the root, where to search for typescript files/project. Default "./"
|
||||
// 3. Ignore: string, comma separated ignore typescript files/project from these directories. Default "" (node_modules are always ignored)
|
||||
// 4. Tsconfig: &typescript.Tsconfig{}, here you can set all compilerOptions if no tsconfig.json exists inside the 'Dir'
|
||||
// 5. Editor: typescript.Editor("username","password"), if setted then alm-tools browser-based typescript IDE will be available. Defailt is nil
|
||||
Options struct {
|
||||
Bin string
|
||||
Dir string
|
||||
Ignore string
|
||||
Tsconfig *Tsconfig
|
||||
Editor *editor.Plugin // the editor is just a plugin also
|
||||
}
|
||||
// Plugin the struct of the Typescript Plugin, holds all necessary fields & methods
|
||||
Plugin struct {
|
||||
options Options
|
||||
// taken from Activate
|
||||
pluginContainer iris.PluginContainer
|
||||
// taken at the PreListen
|
||||
logger *logger.Logger
|
||||
}
|
||||
)
|
||||
|
||||
// Editor is just a shortcut for github.com/kataras/iris/plugin/editor.New()
|
||||
// returns a new (Editor)Plugin, it's exists here because the typescript plugin has direct interest with the EditorPlugin
|
||||
func Editor(username, password string) *editor.Plugin {
|
||||
editorCfg := config.DefaultEditor()
|
||||
editorCfg.Username = username
|
||||
editorCfg.Password = password
|
||||
return editor.New(editorCfg)
|
||||
}
|
||||
|
||||
// DefaultOptions returns the default Options of the Plugin
|
||||
func DefaultOptions() Options {
|
||||
root, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic("Typescript Plugin: Cannot get the Current Working Directory !!! [os.getwd()]")
|
||||
}
|
||||
opt := Options{Dir: root + utils.PathSeparator, Ignore: nodeModules, Tsconfig: DefaultTsconfig()}
|
||||
opt.Bin = npm.Abs("typescript/lib/tsc.js")
|
||||
return opt
|
||||
|
||||
}
|
||||
|
||||
// Plugin
|
||||
|
||||
// New creates & returns a new instnace typescript plugin
|
||||
func New(_opt ...Options) *Plugin {
|
||||
var options = DefaultOptions()
|
||||
|
||||
if _opt != nil && len(_opt) > 0 { //not nil always but I like this way :)
|
||||
opt := _opt[0]
|
||||
|
||||
if opt.Bin != "" {
|
||||
options.Bin = opt.Bin
|
||||
}
|
||||
if opt.Dir != "" {
|
||||
options.Dir = opt.Dir
|
||||
}
|
||||
|
||||
if !strings.Contains(opt.Ignore, nodeModules) {
|
||||
opt.Ignore += "," + nodeModules
|
||||
}
|
||||
|
||||
if opt.Tsconfig != nil {
|
||||
options.Tsconfig = opt.Tsconfig
|
||||
}
|
||||
|
||||
options.Ignore = opt.Ignore
|
||||
}
|
||||
|
||||
return &Plugin{options: options}
|
||||
}
|
||||
|
||||
// implement the IPlugin & IPluginPreListen
|
||||
|
||||
// Activate ...
|
||||
func (t *Plugin) Activate(container iris.PluginContainer) error {
|
||||
t.pluginContainer = container
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetName ...
|
||||
func (t *Plugin) GetName() string {
|
||||
return Name + "[" + utils.RandomString(10) + "]" // this allows the specific plugin to be registed more than one time
|
||||
}
|
||||
|
||||
// GetDescription TypescriptPlugin scans and compile typescript files with ease
|
||||
func (t *Plugin) GetDescription() string {
|
||||
return Name + " scans and compile typescript files with ease. \n"
|
||||
}
|
||||
|
||||
// PreListen ...
|
||||
func (t *Plugin) PreListen(s *iris.Framework) {
|
||||
t.logger = s.Logger
|
||||
t.start()
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// implementation
|
||||
|
||||
func (t *Plugin) start() {
|
||||
defaultCompilerArgs := t.options.Tsconfig.CompilerArgs() //these will be used if no .tsconfig found.
|
||||
if t.hasTypescriptFiles() {
|
||||
//Can't check if permission denied returns always exists = true....
|
||||
//typescriptModule := out + string(os.PathSeparator) + "typescript" + string(os.PathSeparator) + "bin"
|
||||
if !npm.Exists(t.options.Bin) {
|
||||
t.logger.Println("Installing typescript, please wait...")
|
||||
res := npm.Install("typescript")
|
||||
if res.Error != nil {
|
||||
t.logger.Print(res.Error.Error())
|
||||
return
|
||||
}
|
||||
t.logger.Print(res.Message)
|
||||
|
||||
}
|
||||
|
||||
projects := t.getTypescriptProjects()
|
||||
if len(projects) > 0 {
|
||||
watchedProjects := 0
|
||||
//typescript project (.tsconfig) found
|
||||
for _, project := range projects {
|
||||
cmd := utils.CommandBuilder("node", t.options.Bin, "-p", project[0:strings.LastIndex(project, utils.PathSeparator)]) //remove the /tsconfig.json)
|
||||
projectConfig := FromFile(project)
|
||||
|
||||
if projectConfig.CompilerOptions.Watch {
|
||||
watchedProjects++
|
||||
// if has watch : true then we have to wrap the command to a goroutine (I don't want to use the .Start here)
|
||||
go func() {
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
t.logger.Println(err.Error())
|
||||
return
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
t.logger.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
t.logger.Printf("%d Typescript project(s) compiled ( %d monitored by a background file watcher ) ", len(projects), watchedProjects)
|
||||
} else {
|
||||
//search for standalone typescript (.ts) files and compile them
|
||||
files := t.getTypescriptFiles()
|
||||
|
||||
if len(files) > 0 {
|
||||
watchedFiles := 0
|
||||
if t.options.Tsconfig.CompilerOptions.Watch {
|
||||
watchedFiles = len(files)
|
||||
}
|
||||
//it must be always > 0 if we came here, because of if hasTypescriptFiles == true.
|
||||
for _, file := range files {
|
||||
cmd := utils.CommandBuilder("node", t.options.Bin)
|
||||
cmd.AppendArguments(defaultCompilerArgs...)
|
||||
cmd.AppendArguments(file)
|
||||
_, err := cmd.Output()
|
||||
cmd.Args = cmd.Args[0 : len(cmd.Args)-1] //remove the last, which is the file
|
||||
if err != nil {
|
||||
t.logger.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
t.logger.Printf("%d Typescript file(s) compiled ( %d monitored by a background file watcher )", len(files), watchedFiles)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//editor activation
|
||||
if len(projects) == 1 && t.options.Editor != nil {
|
||||
dir := projects[0][0:strings.LastIndex(projects[0], utils.PathSeparator)]
|
||||
t.options.Editor.Dir(dir)
|
||||
t.pluginContainer.Add(t.options.Editor)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Plugin) hasTypescriptFiles() bool {
|
||||
root := t.options.Dir
|
||||
ignoreFolders := strings.Split(t.options.Ignore, ",")
|
||||
hasTs := false
|
||||
|
||||
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
for i := range ignoreFolders {
|
||||
if strings.Contains(path, ignoreFolders[i]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(path, ".ts") {
|
||||
hasTs = true
|
||||
return errors.New("Typescript found, hope that will stop here")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return hasTs
|
||||
}
|
||||
|
||||
func (t *Plugin) getTypescriptProjects() []string {
|
||||
var projects []string
|
||||
ignoreFolders := strings.Split(t.options.Ignore, ",")
|
||||
|
||||
root := t.options.Dir
|
||||
//t.logger.Printf("\nSearching for typescript projects in %s", root)
|
||||
|
||||
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
for i := range ignoreFolders {
|
||||
if strings.Contains(path, ignoreFolders[i]) {
|
||||
//t.logger.Println(path + " ignored")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(path, utils.PathSeparator+"tsconfig.json") {
|
||||
//t.logger.Printf("\nTypescript project found in %s", path)
|
||||
projects = append(projects, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return projects
|
||||
}
|
||||
|
||||
// this is being called if getTypescriptProjects return 0 len, then we are searching for files using that:
|
||||
func (t *Plugin) getTypescriptFiles() []string {
|
||||
var files []string
|
||||
ignoreFolders := strings.Split(t.options.Ignore, ",")
|
||||
|
||||
root := t.options.Dir
|
||||
//t.logger.Printf("\nSearching for typescript files in %s", root)
|
||||
|
||||
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
for i := range ignoreFolders {
|
||||
if strings.Contains(path, ignoreFolders[i]) {
|
||||
//t.logger.Println(path + " ignored")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(path, ".ts") {
|
||||
//t.logger.Printf("\nTypescript file found in %s", path)
|
||||
files = append(files, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return files
|
||||
}
|
||||
|
||||
//
|
||||
//
|
|
@ -1,7 +1,7 @@
|
|||
package sessions
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/iris-contrib/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/iris-contrib/errors"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/gavv/httpexpect/fasthttpexpect"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
var notFoundMessage = "Iris custom message for 404 not found"
|
||||
var internalServerMessage = "Iris custom message for 500 internal server error"
|
||||
|
||||
var routesCustomErrors = []route{
|
||||
// NOT FOUND CUSTOM ERRORS - not registed
|
||||
{"GET", "/test_get_nofound_custom", "/test_get_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"POST", "/test_post_nofound_custom", "/test_post_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"PUT", "/test_put_nofound_custom", "/test_put_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"DELETE", "/test_delete_nofound_custom", "/test_delete_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"HEAD", "/test_head_nofound_custom", "/test_head_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"OPTIONS", "/test_options_nofound_custom", "/test_options_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"CONNECT", "/test_connect_nofound_custom", "/test_connect_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"PATCH", "/test_patch_nofound_custom", "/test_patch_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
{"TRACE", "/test_trace_nofound_custom", "/test_trace_nofound_custom", notFoundMessage, 404, false, nil, nil},
|
||||
// SERVER INTERNAL ERROR 500 PANIC CUSTOM ERRORS - registed
|
||||
{"GET", "/test_get_panic_custom", "/test_get_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"POST", "/test_post_panic_custom", "/test_post_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"PUT", "/test_put_panic_custom", "/test_put_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"DELETE", "/test_delete_panic_custom", "/test_delete_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"HEAD", "/test_head_panic_custom", "/test_head_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"OPTIONS", "/test_options_panic_custom", "/test_options_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"CONNECT", "/test_connect_panic_custom", "/test_connect_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"PATCH", "/test_patch_panic_custom", "/test_patch_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
{"TRACE", "/test_trace_panic_custom", "/test_trace_panic_custom", internalServerMessage, 500, true, nil, nil},
|
||||
}
|
||||
|
||||
func TestCustomErrors(t *testing.T) {
|
||||
api := iris.New()
|
||||
// first register the routes needed
|
||||
for _, r := range routesCustomErrors {
|
||||
if r.Register {
|
||||
api.HandleFunc(r.Method, r.Path, func(ctx *iris.Context) {
|
||||
ctx.EmitError(r.Status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// register the custom errors
|
||||
api.OnError(404, func(ctx *iris.Context) {
|
||||
ctx.Write("%s", notFoundMessage)
|
||||
})
|
||||
|
||||
api.OnError(500, func(ctx *iris.Context) {
|
||||
ctx.Write("%s", internalServerMessage)
|
||||
})
|
||||
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.NoListen().Handler),
|
||||
})
|
||||
|
||||
// run the tests
|
||||
for _, r := range routesCustomErrors {
|
||||
e.Request(r.Method, r.RequestPath).
|
||||
Expect().
|
||||
Status(r.Status).Body().Equal(r.Body)
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/gavv/httpexpect/fasthttpexpect"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
func TestSimpleParty(t *testing.T) {
|
||||
h := func(c *iris.Context) { c.WriteString(c.HostString() + c.PathString()) }
|
||||
|
||||
/*
|
||||
// subdomain first, but this test will fail on your machine, so I just commend it, you can imagine what will be
|
||||
party2 := iris.Party("kataras.")
|
||||
{
|
||||
party2.Get("/", h)
|
||||
party2.Get("/path1", h)
|
||||
party2.Get("/path2", h)
|
||||
party2.Get("/namedpath/:param1/something/:param2", h)
|
||||
party2.Get("/namedpath/:param1/something/:param2/else", h)
|
||||
}*/
|
||||
|
||||
// simple
|
||||
party1 := iris.Party("/party1")
|
||||
{
|
||||
party1.Get("/", h)
|
||||
party1.Get("/path1", h)
|
||||
party1.Get("/path2", h)
|
||||
party1.Get("/namedpath/:param1/something/:param2", h)
|
||||
party1.Get("/namedpath/:param1/something/:param2/else", h)
|
||||
}
|
||||
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(iris.NoListen().Handler),
|
||||
})
|
||||
|
||||
request := func(reqPath string) {
|
||||
e.Request("GET", reqPath).
|
||||
Expect().
|
||||
Status(iris.StatusOK).Body().Equal(reqPath)
|
||||
}
|
||||
|
||||
// run the tests
|
||||
request("/party1/")
|
||||
request("/party1/path1")
|
||||
request("/party1/path2")
|
||||
request("/party1/namedpath/theparam1/something/theparam2")
|
||||
request("/party1/namedpath/theparam1/something/theparam2/else")
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/gavv/httpexpect/fasthttpexpect"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
type param struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
type route struct {
|
||||
Method string
|
||||
Path string
|
||||
RequestPath string
|
||||
Body string
|
||||
Status int
|
||||
Register bool
|
||||
Params []param
|
||||
UrlParams []param
|
||||
}
|
||||
|
||||
var routes = []route{
|
||||
// FOUND - registed
|
||||
{"GET", "/test_get", "/test_get", "hello, get!", 200, true, nil, nil},
|
||||
{"POST", "/test_post", "/test_post", "hello, post!", 200, true, nil, nil},
|
||||
{"PUT", "/test_put", "/test_put", "hello, put!", 200, true, nil, nil},
|
||||
{"DELETE", "/test_delete", "/test_delete", "hello, delete!", 200, true, nil, nil},
|
||||
{"HEAD", "/test_head", "/test_head", "hello, head!", 200, true, nil, nil},
|
||||
{"OPTIONS", "/test_options", "/test_options", "hello, options!", 200, true, nil, nil},
|
||||
{"CONNECT", "/test_connect", "/test_connect", "hello, connect!", 200, true, nil, nil},
|
||||
{"PATCH", "/test_patch", "/test_patch", "hello, patch!", 200, true, nil, nil},
|
||||
{"TRACE", "/test_trace", "/test_trace", "hello, trace!", 200, true, nil, nil},
|
||||
// NOT FOUND - not registed
|
||||
{"GET", "/test_get_nofound", "/test_get_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"POST", "/test_post_nofound", "/test_post_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"PUT", "/test_put_nofound", "/test_put_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"DELETE", "/test_delete_nofound", "/test_delete_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"HEAD", "/test_head_nofound", "/test_head_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"OPTIONS", "/test_options_nofound", "/test_options_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"CONNECT", "/test_connect_nofound", "/test_connect_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"PATCH", "/test_patch_nofound", "/test_patch_nofound", "Not Found", 404, false, nil, nil},
|
||||
{"TRACE", "/test_trace_nofound", "/test_trace_nofound", "Not Found", 404, false, nil, nil},
|
||||
// Parameters
|
||||
{"GET", "/test_get_parameter1/:name", "/test_get_parameter1/iris", "name=iris", 200, true, []param{{"name", "iris"}}, nil},
|
||||
{"GET", "/test_get_parameter2/:name/details/:something", "/test_get_parameter2/iris/details/anything", "name=iris,something=anything", 200, true, []param{{"name", "iris"}, {"something", "anything"}}, nil},
|
||||
{"GET", "/test_get_parameter2/:name/details/:something/*else", "/test_get_parameter2/iris/details/anything/elsehere", "name=iris,something=anything,else=/elsehere", 200, true, []param{{"name", "iris"}, {"something", "anything"}, {"else", "elsehere"}}, nil},
|
||||
// URL Parameters
|
||||
{"GET", "/test_get_urlparameter1/first", "/test_get_urlparameter1/first?name=irisurl", "name=irisurl", 200, true, nil, []param{{"name", "irisurl"}}},
|
||||
{"GET", "/test_get_urlparameter2/second", "/test_get_urlparameter2/second?name=irisurl&something=anything", "name=irisurl,something=anything", 200, true, nil, []param{{"name", "irisurl"}, {"something", "anything"}}},
|
||||
{"GET", "/test_get_urlparameter2/first/second/third", "/test_get_urlparameter2/first/second/third?name=irisurl&something=anything&else=elsehere", "name=irisurl,something=anything,else=elsehere", 200, true, nil, []param{{"name", "irisurl"}, {"something", "anything"}, {"else", "elsehere"}}},
|
||||
}
|
||||
|
||||
func TestRouter(t *testing.T) {
|
||||
api := iris.New()
|
||||
for idx := range routes {
|
||||
r := routes[idx]
|
||||
if r.Register {
|
||||
api.HandleFunc(r.Method, r.Path, func(ctx *iris.Context) {
|
||||
ctx.SetStatusCode(r.Status)
|
||||
if r.Params != nil && len(r.Params) > 0 {
|
||||
ctx.SetBodyString(ctx.Params.String())
|
||||
} else if r.UrlParams != nil && len(r.UrlParams) > 0 {
|
||||
if len(r.UrlParams) != len(ctx.URLParams()) {
|
||||
t.Fatalf("Error when comparing length of url parameters %d != %d", len(r.UrlParams), len(ctx.URLParams()))
|
||||
}
|
||||
paramsKeyVal := ""
|
||||
for idxp, p := range r.UrlParams {
|
||||
val := ctx.URLParam(p.Key)
|
||||
paramsKeyVal += p.Key + "=" + val + ","
|
||||
if idxp == len(r.UrlParams)-1 {
|
||||
paramsKeyVal = paramsKeyVal[0 : len(paramsKeyVal)-1]
|
||||
}
|
||||
}
|
||||
ctx.SetBodyString(paramsKeyVal)
|
||||
} else {
|
||||
ctx.SetBodyString(r.Body)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.NoListen().Handler),
|
||||
})
|
||||
|
||||
// run the tests (1)
|
||||
for idx := range routes {
|
||||
r := routes[idx]
|
||||
e.Request(r.Method, r.RequestPath).
|
||||
Expect().
|
||||
Status(r.Status).Body().Equal(r.Body)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPathEscape(t *testing.T) {
|
||||
api := iris.New()
|
||||
|
||||
api.Get("/details/:name", func(ctx *iris.Context) {
|
||||
name := ctx.Param("name")
|
||||
highlight := ctx.URLParam("highlight")
|
||||
ctx.Text(iris.StatusOK, fmt.Sprintf("name=%s,highlight=%s", name, highlight))
|
||||
})
|
||||
|
||||
e := httpexpect.WithConfig(httpexpect.Config{Reporter: httpexpect.NewAssertReporter(t), Client: fasthttpexpect.NewBinder(api.NoListen().Handler)})
|
||||
|
||||
e.Request("GET", "/details/Sakamoto desu ga?highlight=text").Expect().Status(iris.StatusOK).Body().Equal("name=Sakamoto desu ga,highlight=text")
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Package tests empty
|
||||
/*Why empty?
|
||||
The only reason I don't make unit tests is because I think the whole story here is wrong. All unit tests may succed but in practise the app fail.
|
||||
believe in the real micro example-usage-tests,
|
||||
but if you have different opinion make PRs here.
|
||||
*/
|
||||
/*Alternative:
|
||||
If you want to test your API use this, new, library ,which after my suggestion has fasthttp & Iris support: https://github.com/gavv/httpexpect
|
||||
I have added some test examples to this directory also in order to help you
|
||||
*/
|
||||
package tests
|
||||
|
||||
// run all verbose mode:
|
||||
// go test -v
|
||||
// run all:
|
||||
// go test .
|
||||
// run specific:
|
||||
// go test -run "Router"
|
|
@ -1,7 +1,7 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/iris-contrib/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
Loading…
Reference in New Issue
Block a user