diff --git a/README.md b/README.md index 3df6a383e05..658f1e34257 100644 --- a/README.md +++ b/README.md @@ -25,49 +25,71 @@ the latest master builds [here](https://grafana.com/grafana/download) ### Dependencies - Go (Latest Stable) + - bra [`go get github.com/Unknwon/bra`] - Node.js LTS + - yarn [`npm install -g yarn`] + +### Get the project + +**The project located in the go-path will be your working directory.** -### Building the backend ```bash go get github.com/grafana/grafana cd $GOPATH/src/github.com/grafana/grafana +``` + +### Building + +#### The backend + +```bash go run build.go setup go run build.go build ``` -### Building frontend assets +#### Frontend assets -For this you need Node.js (LTS version). +*For this you need Node.js (LTS version).* -To build the assets, rebuild on file change, and serve them by Grafana's webserver (http://localhost:3000): ```bash -npm install -g yarn yarn install --pure-lockfile +``` + +### Run and rebuild on source change + +#### Backend + +To run the backend and rebuild on source change: + +```bash +$GOPATH/bin/bra run +``` + +#### Frontend + +Rebuild on file change, and serve them by Grafana's webserver (http://localhost:3000): + +```bash yarn watch ``` Build the assets, rebuild on file change with Hot Module Replacement (HMR), and serve them by webpack-dev-server (http://localhost:3333): + ```bash yarn start # OR set a theme env GRAFANA_THEME=light yarn start ``` -Note: HMR for Angular is not supported. If you edit files in the Angular part of the app, the whole page will reload. -Run tests +*Note: HMR for Angular is not supported. If you edit files in the Angular part of the app, the whole page will reload.* + +Run tests and rebuild on source change: + ```bash yarn jest ``` -### Recompile backend on source change - -To rebuild on source change. -```bash -go get github.com/Unknwon/bra -bra run -``` - -Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`). +**Open grafana in your browser (default: e.g. `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).** ### Building a Docker image diff --git a/conf/defaults.ini b/conf/defaults.ini index 788112ae67e..d021d342fbf 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -113,6 +113,9 @@ cache_mode = private # Login cookie name cookie_name = grafana_session +# Login cookie same site setting. defaults to `lax`. can be set to "lax", "strict" and "none" +cookie_samesite = lax + # How many days an session can be unused before we inactivate it login_remember_days = 7 diff --git a/conf/sample.ini b/conf/sample.ini index 89880106345..ef677320686 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -109,6 +109,9 @@ log_queries = # Login cookie name ;cookie_name = grafana_session +# Login cookie same site setting. defaults to `lax`. can be set to "lax", "strict" and "none" +;cookie_samesite = lax + # How many days an session can be unused before we inactivate it ;login_remember_days = 7 diff --git a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss index 46eed5f7ff1..b07fe2433c9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss +++ b/packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss @@ -167,6 +167,7 @@ $arrowSize: 15px; color: inherit; padding: 0; border-radius: 10px; + cursor: pointer; } .sp-replacer:hover, diff --git a/packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx b/packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx index 7ce4b8335ff..8516760d6f3 100644 --- a/packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx +++ b/packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx @@ -1,26 +1,38 @@ // Libraries -import React, { SFC } from 'react'; +import React, { FunctionComponent } from 'react'; interface Props { title?: string; onClose?: () => void; - children: JSX.Element | JSX.Element[]; + children: JSX.Element | JSX.Element[] | boolean; + onAdd?: () => void; } -export const PanelOptionsGroup: SFC = props => { +export const PanelOptionsGroup: FunctionComponent = props => { return (
- {props.title && ( + {props.onAdd ? (
- {props.title} - {props.onClose && ( - - )} +
+ ) : ( + props.title && ( +
+ {props.title} + {props.onClose && ( + + )} +
+ ) )} -
{props.children}
+ {props.children &&
{props.children}
}
); }; diff --git a/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss b/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss index cfc832afa98..b5b815cf57c 100644 --- a/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss +++ b/packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss @@ -7,18 +7,57 @@ .panel-options-group__header { padding: 4px 8px; - font-size: 1.1rem; background: $panel-options-group-header-bg; position: relative; border-radius: $border-radius $border-radius 0 0; + display: flex; + align-items: center; .btn { position: absolute; right: 0; - top: 0px; + top: 0; } } +.panel-options-group__add-btn { + background: none; + border: none; + display: flex; + align-items: center; + padding: 0; + + &:hover { + .panel-options-group__add-circle { + background-color: $btn-success-bg; + color: $text-color-strong; + } + } +} + +.panel-options-group__add-circle { + @include gradientBar($btn-success-bg, $btn-success-bg-hl, $text-color); + + border-radius: 50px; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 6px; + + i { + position: relative; + top: 1px; + } +} + +.panel-options-group__title { + font-size: 1.1rem; + position: relative; + top: 1px; +} + .panel-options-group__body { padding: 20px; diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss index 61278321572..200adfbfd75 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss +++ b/packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss @@ -1,11 +1,11 @@ .thresholds { - margin-bottom: 10px; + margin-bottom: 20px; } .thresholds-row { display: flex; flex-direction: row; - height: 70px; + height: 62px; } .thresholds-row:first-child > .thresholds-row-color-indicator { @@ -21,21 +21,21 @@ } .thresholds-row-add-button { + @include buttonBackground($btn-success-bg, $btn-success-bg-hl, $text-color); + align-self: center; margin-right: 5px; - color: $green; height: 24px; width: 24px; - background-color: $green; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; -} -.thresholds-row-add-button > i { - color: $white; + &:hover { + color: $text-color-strong; + } } .thresholds-row-color-indicator { diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.test.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.test.tsx index bbad3e5a7ca..caa09c9e5ff 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.test.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { ValueMappingsEditor, Props } from './ValueMappingsEditor'; -import { MappingType } from '../../types/panel'; +import { MappingType } from '../../types'; const setup = (propOverrides?: object) => { const props: Props = { diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx index ca0a6e71f4a..f9646781048 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx @@ -1,8 +1,8 @@ import React, { PureComponent } from 'react'; import MappingRow from './MappingRow'; -import { MappingType, ValueMapping } from '../../types/panel'; -import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup'; +import { MappingType, ValueMapping } from '../../types'; +import { PanelOptionsGroup } from '..'; export interface Props { valueMappings: ValueMapping[]; @@ -81,8 +81,7 @@ export class ValueMappingsEditor extends PureComponent { const { valueMappings } = this.state; return ( - -
+ {valueMappings.length > 0 && valueMappings.map((valueMapping, index) => ( { removeValueMapping={() => this.onRemoveMapping(valueMapping.id)} /> ))} -
-
-
- -
-
Add mapping
-
); } diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/__snapshots__/ValueMappingsEditor.test.tsx.snap b/packages/grafana-ui/src/components/ValueMappingsEditor/__snapshots__/ValueMappingsEditor.test.tsx.snap index 8a465ff88df..b0dd7d81840 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/__snapshots__/ValueMappingsEditor.test.tsx.snap +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/__snapshots__/ValueMappingsEditor.test.tsx.snap @@ -2,55 +2,37 @@ exports[`Render should render component 1`] = ` -
- - + -
-
-
- -
-
- Add mapping -
-
+ } + />
`; diff --git a/pkg/services/auth/auth_token.go b/pkg/services/auth/auth_token.go index db4d9d18624..98687f2013d 100644 --- a/pkg/services/auth/auth_token.go +++ b/pkg/services/auth/auth_token.go @@ -96,6 +96,7 @@ func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, va Path: setting.AppSubUrl + "/", Secure: s.Cfg.SecurityHTTPSCookies, MaxAge: maxAge, + SameSite: s.Cfg.LoginCookieSameSite, } http.SetCookie(ctx.Resp, &cookie) diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index cf486a228ab..c3c78d10fec 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -6,6 +6,7 @@ package setting import ( "bytes" "fmt" + "net/http" "net/url" "os" "path" @@ -227,6 +228,7 @@ type Cfg struct { LoginCookieMaxDays int LoginCookieRotation int LoginDeleteExpiredTokensAfterDays int + LoginCookieSameSite http.SameSite SecurityHTTPSCookies bool } @@ -557,6 +559,20 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { cfg.LoginCookieName = login.Key("cookie_name").MustString("grafana_session") cfg.LoginCookieMaxDays = login.Key("login_remember_days").MustInt(7) cfg.LoginDeleteExpiredTokensAfterDays = login.Key("delete_expired_token_after_days").MustInt(30) + + samesiteString := login.Key("cookie_samesite").MustString("lax") + validSameSiteValues := map[string]http.SameSite{ + "lax": http.SameSiteLaxMode, + "strict": http.SameSiteStrictMode, + "none": http.SameSiteDefaultMode, + } + + if samesite, ok := validSameSiteValues[samesiteString]; ok { + cfg.LoginCookieSameSite = samesite + } else { + cfg.LoginCookieSameSite = http.SameSiteLaxMode + } + cfg.LoginCookieRotation = login.Key("rotate_token_minutes").MustInt(10) if cfg.LoginCookieRotation < 2 { cfg.LoginCookieRotation = 2 diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 989746fd067..ed321c6a69e 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -249,7 +249,7 @@ export class KeybindingSrv { if (panelInfo.panel.legend) { const panelRef = dashboard.getPanelById(dashboard.meta.focusPanelId); panelRef.legend.show = !panelRef.legend.show; - panelRef.refresh(); + panelRef.render(); } } }); diff --git a/public/app/features/datasources/DataSourcesListItem.tsx b/public/app/features/datasources/DataSourcesListItem.tsx index 157e9447852..d0ff69a66ee 100644 --- a/public/app/features/datasources/DataSourcesListItem.tsx +++ b/public/app/features/datasources/DataSourcesListItem.tsx @@ -16,12 +16,12 @@ export class DataSourcesListItem extends PureComponent {
- + {dataSource.name}
{dataSource.name} - {dataSource.isDefault && default} + {dataSource.isDefault && default}
{dataSource.url}
diff --git a/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap index a424276cf32..3ab1b1d53aa 100644 --- a/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap +++ b/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap @@ -24,6 +24,7 @@ exports[`Render should render component 1`] = ` className="card-item-figure" > gdev-cloudwatch diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index f72b441b67b..68d982eab13 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -281,7 +281,7 @@ class GraphCtrl extends MetricsPanelCtrl { toggleLegend() { this.panel.legend.show = !this.panel.legend.show; - this.refresh(); + this.render(); } legendValuesOptionChanged() { diff --git a/public/app/plugins/panel/graph/tab_legend.html b/public/app/plugins/panel/graph/tab_legend.html index 82e1335dc10..b9c45447263 100644 --- a/public/app/plugins/panel/graph/tab_legend.html +++ b/public/app/plugins/panel/graph/tab_legend.html @@ -3,7 +3,7 @@
Options
+ checked="ctrl.panel.legend.show" on-change="ctrl.render()">