Embrace the weekend- Update to rc.3 | Read the HISTORY.md

This commit is contained in:
Makis Maropoulos 2016-06-24 01:34:49 +03:00
parent 4a446ac1e2
commit f83b532835
50 changed files with 142 additions and 3812 deletions

View File

@ -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:

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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(),
}
}

View File

@ -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
}

View File

@ -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),
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
)

View File

@ -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.**

View File

@ -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...)
}

View File

@ -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)
}
```

View File

@ -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()
}

View File

@ -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

View File

@ -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
View File

@ -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------------------------------

View File

@ -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.

View File

@ -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...)
}
}

View File

@ -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
}

View File

@ -1,2 +0,0 @@
# Middleware
Iris has its tiny middlewares here.

View File

@ -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
}
}

View File

@ -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")
}
```

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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")
}
```

View File

@ -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...)
}

View File

@ -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")
}
```

View File

@ -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
}

View File

@ -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"

View File

@ -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(...)

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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) {}

View File

@ -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

View File

@ -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"},
}
}

View File

@ -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
}
//
//

View File

@ -1,7 +1,7 @@
package sessions
import (
"github.com/kataras/iris/errors"
"github.com/iris-contrib/errors"
)
var (

View File

@ -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 (

View File

@ -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)
}
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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"

View File

@ -1,7 +1,7 @@
package utils
import (
"github.com/kataras/iris/errors"
"github.com/iris-contrib/errors"
)
var (