From 8b8f3226307990703ac6a61ba2d4c6183099e99c Mon Sep 17 00:00:00 2001 From: William Petit Date: Mon, 13 Jul 2020 18:49:44 +0200 Subject: [PATCH] =?UTF-8?q?R=C3=A9cup=C3=A9ration=20automatique=20du=20pro?= =?UTF-8?q?fil=20au=20lancement=20de=20l'application?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 198 +++++++++++++++++- client/package.json | 9 + client/src/components/HomePage/HomePage.tsx | 4 +- client/src/config.ts | 1 + client/src/store/reducers/auth.ts | 4 +- client/src/store/sagas/init.ts | 7 +- client/src/store/sagas/root.ts | 1 + client/src/store/sagas/users.ts | 8 +- client/src/types/user.ts | 5 +- client/src/util/daddy.ts | 74 +++++-- go.mod | 2 + go.sum | 2 + internal/config/config.go | 24 ++- internal/route/logout.go | 7 + internal/route/mount.go | 32 ++- .../session/{middleware.go => user_email.go} | 17 ++ 16 files changed, 349 insertions(+), 46 deletions(-) rename internal/session/{middleware.go => user_email.go} (86%) diff --git a/client/package-lock.json b/client/package-lock.json index 1e597da..635b1c5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1230,8 +1230,7 @@ "@types/node": { "version": "13.13.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz", - "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==", - "dev": true + "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==" }, "@types/prop-types": { "version": "15.7.3", @@ -1372,6 +1371,11 @@ } } }, + "@types/zen-observable": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", + "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" + }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -1547,6 +1551,23 @@ "@xtuc/long": "4.2.2" } }, + "@wry/context": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.4.4.tgz", + "integrity": "sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag==", + "requires": { + "@types/node": ">=6", + "tslib": "^1.9.3" + } + }, + "@wry/equality": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz", + "integrity": "sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA==", + "requires": { + "tslib": "^1.9.3" + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -1723,6 +1744,93 @@ } } }, + "apollo-cache": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz", + "integrity": "sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA==", + "requires": { + "apollo-utilities": "^1.3.4", + "tslib": "^1.10.0" + } + }, + "apollo-cache-inmemory": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.6.tgz", + "integrity": "sha512-L8pToTW/+Xru2FFAhkZ1OA9q4V4nuvfoPecBM34DecAugUZEBhI2Hmpgnzq2hTKZ60LAMrlqiASm0aqAY6F8/A==", + "requires": { + "apollo-cache": "^1.3.5", + "apollo-utilities": "^1.3.4", + "optimism": "^0.10.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0" + } + }, + "apollo-client": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.10.tgz", + "integrity": "sha512-jiPlMTN6/5CjZpJOkGeUV0mb4zxx33uXWdj/xQCfAMkuNAC3HN7CvYDyMHHEzmcQ5GV12LszWoQ/VlxET24CtA==", + "requires": { + "@types/zen-observable": "^0.8.0", + "apollo-cache": "1.3.5", + "apollo-link": "^1.0.0", + "apollo-utilities": "1.3.4", + "symbol-observable": "^1.0.2", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0", + "zen-observable": "^0.8.0" + } + }, + "apollo-link": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz", + "integrity": "sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==", + "requires": { + "apollo-utilities": "^1.3.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable-ts": "^0.8.21" + } + }, + "apollo-link-http": { + "version": "1.5.17", + "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz", + "integrity": "sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg==", + "requires": { + "apollo-link": "^1.2.14", + "apollo-link-http-common": "^0.2.16", + "tslib": "^1.9.3" + } + }, + "apollo-link-http-common": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz", + "integrity": "sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==", + "requires": { + "apollo-link": "^1.2.14", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "apollo-link-ws": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/apollo-link-ws/-/apollo-link-ws-1.0.20.tgz", + "integrity": "sha512-mjSFPlQxmoLArpHBeUb2Xj+2HDYeTaJqFGOqQ+I8NVJxgL9lJe84PDWcPah/yMLv3rB7QgBDSuZ0xoRFBPlySw==", + "requires": { + "apollo-link": "^1.2.14", + "tslib": "^1.9.3" + } + }, + "apollo-utilities": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz", + "integrity": "sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.10.0" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -1875,8 +1983,7 @@ "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "asynckit": { "version": "0.4.0", @@ -2724,6 +2831,11 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -5029,8 +5141,7 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fastparse": { "version": "1.1.2", @@ -5499,11 +5610,21 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, + "graphql": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz", + "integrity": "sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==" + }, "graphql-request": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-2.0.0.tgz", "integrity": "sha512-Ww3Ax+G3l2d+mPT8w7HC9LfrKjutnCKtnDq7ZZp2ghVk5IQDjwAk3/arRF1ix17Ky15rm0hrSKVKxRhIVlSuoQ==" }, + "graphql-tag": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.4.tgz", + "integrity": "sha512-O7vG5BT3w6Sotc26ybcvLKNTdfr4GfsIVMD+LdYqXCeJIYPRyp8BIsDOUtxw7S1PYvRw5vH3278J2EDezR6mfA==" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -6308,6 +6429,11 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + }, "js-base64": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", @@ -7340,6 +7466,14 @@ "is-wsl": "^1.1.0" } }, + "optimism": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.10.3.tgz", + "integrity": "sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw==", + "requires": { + "@wry/context": "^0.4.0" + } + }, "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -9420,6 +9554,33 @@ "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==" }, + "subscriptions-transport-ws": { + "version": "0.9.17", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.17.tgz", + "integrity": "sha512-hNHi2N80PBz4T0V0QhnnsMGvG3XDFDS9mS6BhZ3R12T6EBywC8d/uJscsga0cVO4DKtXCkCRrWm2sOYrbOdhEA==", + "requires": { + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -9658,6 +9819,14 @@ "glob": "^7.1.2" } }, + "ts-invariant": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz", + "integrity": "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==", + "requires": { + "tslib": "^1.9.3" + } + }, "ts-loader": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-7.0.5.tgz", @@ -9725,8 +9894,7 @@ "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" }, "tty-browserify": { "version": "0.0.0", @@ -10833,6 +11001,20 @@ "dev": true } } + }, + "zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "zen-observable-ts": { + "version": "0.8.21", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz", + "integrity": "sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg==", + "requires": { + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } } } } diff --git a/client/package.json b/client/package.json index 6f0d883..51f5561 100644 --- a/client/package.json +++ b/client/package.json @@ -52,9 +52,17 @@ }, "dependencies": { "@types/qs": "^6.9.3", + "apollo-cache-inmemory": "^1.6.6", + "apollo-client": "^2.6.10", + "apollo-link": "^1.2.14", + "apollo-link-http": "^1.5.17", + "apollo-link-ws": "^1.0.20", + "apollo-utilities": "^1.3.4", "bulma": "^0.7.2", "bulma-switch": "^2.0.0", + "graphql": "^15.3.0", "graphql-request": "^2.0.0", + "graphql-tag": "^2.10.4", "jwt-decode": "^2.2.0", "qs": "^6.9.4", "react": "^16.12.0", @@ -65,6 +73,7 @@ "redux": "^4.0.4", "redux-saga": "^1.1.3", "styled-components": "^4.4.1", + "subscriptions-transport-ws": "^0.9.17", "typescript": "^3.8.3" } } diff --git a/client/src/components/HomePage/HomePage.tsx b/client/src/components/HomePage/HomePage.tsx index b9ac584..cae00e9 100644 --- a/client/src/components/HomePage/HomePage.tsx +++ b/client/src/components/HomePage/HomePage.tsx @@ -14,8 +14,8 @@ export function HomePage() {
{ - currentUser && currentUser.full_name ? -

Bonjour {currentUser.full_name} !

: + currentUser && currentUser.email ? +

Bonjour {currentUser.email} !

:

Veuillez vous authentifier.

}
diff --git a/client/src/config.ts b/client/src/config.ts index d8f51fe..51b9803 100644 --- a/client/src/config.ts +++ b/client/src/config.ts @@ -2,6 +2,7 @@ export const Config = { loginURL: get("loginURL", "http://localhost:8081/login"), logoutURL: get("logoutURL", "http://localhost:8081/logout"), graphQLEndpoint: get("graphQLEndpoint", "http://localhost:8081/api/v1/graphql"), + subscriptionEndpoint: get("subscriptionEndpoint", "ws://localhost:8081/api/v1/graphql"), }; function get(key: string, defaultValue: T):T { diff --git a/client/src/store/reducers/auth.ts b/client/src/store/reducers/auth.ts index b25f0c4..b190d1b 100644 --- a/client/src/store/reducers/auth.ts +++ b/client/src/store/reducers/auth.ts @@ -39,7 +39,9 @@ function handleFetchProfileSuccess(state: AuthState, { profile }: fetchProfileSu ...state, isAuthenticated: true, currentUser: { - ...profile, + email: profile.email, + connectedAt: profile.connectedAt, + createdAt: profile.createdAt, } }; }; \ No newline at end of file diff --git a/client/src/store/sagas/init.ts b/client/src/store/sagas/init.ts index fdd4fa2..b809870 100644 --- a/client/src/store/sagas/init.ts +++ b/client/src/store/sagas/init.ts @@ -1,7 +1,12 @@ import { all, put } from "redux-saga/effects"; +import { fetchProfile } from "../actions/profile"; export function* initRootSaga() { yield all([ - + fetchUserProfileSaga(), ]); +} + +export function* fetchUserProfileSaga() { + yield put(fetchProfile()); } \ No newline at end of file diff --git a/client/src/store/sagas/root.ts b/client/src/store/sagas/root.ts index 3b29c86..562e7c9 100644 --- a/client/src/store/sagas/root.ts +++ b/client/src/store/sagas/root.ts @@ -7,5 +7,6 @@ export function* rootSaga() { yield all([ initRootSaga(), failureRootSaga(), + usersRootSaga(), ]); } diff --git a/client/src/store/sagas/users.ts b/client/src/store/sagas/users.ts index 42f0ac2..62952ae 100644 --- a/client/src/store/sagas/users.ts +++ b/client/src/store/sagas/users.ts @@ -1,4 +1,4 @@ -import { DaddyClient } from "../../util/daddy"; +import { DaddyClient, getClient } from "../../util/daddy"; import { Config } from "../../config"; import { all, takeLatest, put, select } from "redux-saga/effects"; import { FETCH_PROFILE_REQUEST, fetchProfile, FETCH_PROFILE_FAILURE, FETCH_PROFILE_SUCCESS } from "../actions/profile"; @@ -18,12 +18,12 @@ export function* onCurrentUserChangeSaga() { } export function* fetchProfileSaga() { - const client = new DaddyClient(Config.graphQLEndpoint); + const client = getClient(Config.graphQLEndpoint, Config.subscriptionEndpoint); let profile: User; try { - const currentUser: User = yield select((state: RootState) => state.auth.currentUser); - profile = yield client.fetchUser(currentUser.email).then(result => result.user); + profile = yield client.fetchProfile().then(result => result.userProfile); + console.log(profile); } catch(err) { yield put({ type: FETCH_PROFILE_FAILURE, err }); return; diff --git a/client/src/types/user.ts b/client/src/types/user.ts index 13c920d..f05b6c3 100644 --- a/client/src/types/user.ts +++ b/client/src/types/user.ts @@ -1,6 +1,5 @@ export interface User { email: string - full_name?: string - updated_at?: Date - created_at?: Date + connectedAt?: Date + createdAt?: Date } \ No newline at end of file diff --git a/client/src/util/daddy.ts b/client/src/util/daddy.ts index 9d3d104..0d7ea57 100644 --- a/client/src/util/daddy.ts +++ b/client/src/util/daddy.ts @@ -1,5 +1,10 @@ -import { GraphQLClient } from 'graphql-request' -import { Config } from "../config"; +import ApolloClient from 'apollo-client'; +import { InMemoryCache } from 'apollo-cache-inmemory'; +import { split } from 'apollo-link'; +import { HttpLink } from 'apollo-link-http'; +import { WebSocketLink } from 'apollo-link-ws'; +import { getMainDefinition } from 'apollo-utilities'; +import gql from 'graphql-tag'; export class UnauthorizedError extends Error { constructor(...args: any[]) { @@ -8,30 +13,65 @@ export class UnauthorizedError extends Error { } } +let client: DaddyClient + +export function getClient(graphQLEndpoint: string, subscriptionEndpoint: string): DaddyClient { + if (!client) { + client = new DaddyClient(graphQLEndpoint, subscriptionEndpoint); + } + + return client; +} + export class DaddyClient { - gql: GraphQLClient + gql: ApolloClient - constructor(endpoint: string) { - this.gql = new GraphQLClient(endpoint, { - headers: { - mode: 'cors', + constructor(graphQLEndpoint: string, subscriptionEndpoint: string) { + const wsLink = new WebSocketLink({ + uri: subscriptionEndpoint, + options: { + reconnect: true } }); + + const httpLink = new HttpLink({ + uri: graphQLEndpoint, + fetchOptions: { + mode: 'cors', + credentials: 'include', + } + }); + + const link = split( + ({ query }) => { + const definition = getMainDefinition(query); + return ( + definition.kind === 'OperationDefinition' && + definition.operation === 'subscription' + ); + }, + wsLink, + httpLink, + ); + + this.gql = new ApolloClient({ + link: link, + cache: new InMemoryCache(), + }); } - fetchUser(email: string) { - return this.gql.rawRequest(` - query fetchUser { - user(where: {email: {eq: $email}}) { - id - created_at - updated_at + fetchProfile() { + return this.gql.query({ + query: gql` + query { + userProfile { email, - full_name + createdAt, + connectedAt } - } - `, { email }) + }` + }) .then(this.assertAuthorization) } diff --git a/go.mod b/go.mod index f530b00..3e5fecd 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,11 @@ require ( github.com/cortesi/modd v0.0.0-20200630120222-8983974e5450 // indirect github.com/go-chi/chi v4.1.0+incompatible github.com/gorilla/sessions v1.2.0 + github.com/gorilla/websocket v1.2.0 github.com/jackc/pgx v3.6.2+incompatible github.com/jackc/pgx/v4 v4.7.1 github.com/pkg/errors v0.9.1 + github.com/rs/cors v1.7.0 github.com/vektah/gqlparser/v2 v2.0.1 gitlab.com/wpetit/goweb v0.0.0-20200707070104-985ce3eba3c2 gopkg.in/yaml.v2 v2.2.8 diff --git a/go.sum b/go.sum index 385b725..3f82143 100644 --- a/go.sum +++ b/go.sum @@ -248,6 +248,8 @@ github.com/rjeczalik/notify v0.0.0-20181126183243-629144ba06a1 h1:FLWDC+iIP9BWgY github.com/rjeczalik/notify v0.0.0-20181126183243-629144ba06a1/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= diff --git a/internal/config/config.go b/internal/config/config.go index cccb1d2..ba0d750 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,13 +37,19 @@ func NewFromFile(filepath string) (*Config, error) { } type HTTPConfig struct { - Address string `yaml:"address" env:"HTTP_ADDRESS"` - CookieAuthenticationKey string `yaml:"cookieAuthenticationKey" env:"HTTP_COOKIE_AUTHENTICATION_KEY"` - CookieEncryptionKey string `yaml:"cookieEncryptionKey" env:"HTTP_COOKIE_ENCRYPTION_KEY"` - CookieMaxAge int `yaml:"cookieMaxAge" env:"HTTP_COOKIE_MAX_AGE"` - TemplateDir string `yaml:"templateDir" env:"HTTP_TEMPLATE_DIR"` - PublicDir string `yaml:"publicDir" env:"HTTP_PUBLIC_DIR"` - FrontendURL string `yaml:"frontendURL" env:"HTTP_FRONTEND_URL"` + Address string `yaml:"address" env:"HTTP_ADDRESS"` + CookieAuthenticationKey string `yaml:"cookieAuthenticationKey" env:"HTTP_COOKIE_AUTHENTICATION_KEY"` + CookieEncryptionKey string `yaml:"cookieEncryptionKey" env:"HTTP_COOKIE_ENCRYPTION_KEY"` + CookieMaxAge int `yaml:"cookieMaxAge" env:"HTTP_COOKIE_MAX_AGE"` + TemplateDir string `yaml:"templateDir" env:"HTTP_TEMPLATE_DIR"` + PublicDir string `yaml:"publicDir" env:"HTTP_PUBLIC_DIR"` + FrontendURL string `yaml:"frontendURL" env:"HTTP_FRONTEND_URL"` + CORS CORSConfig `yaml:"cors"` +} + +type CORSConfig struct { + AllowedOrigins []string `yaml:"allowedOrigins" env:"HTTP_CORS_ALLOWED_ORIGINS"` + AllowCredentials bool `yaml:"allowCredentials" env:"HTTP_CORS_ALLOW_CREDENTIALS"` } type OIDCConfig struct { @@ -83,6 +89,10 @@ func NewDefault() *Config { TemplateDir: "template", PublicDir: "public", FrontendURL: "http://localhost:8080", + CORS: CORSConfig{ + AllowedOrigins: []string{"http://localhost:8080"}, + AllowCredentials: true, + }, }, OIDC: OIDCConfig{ IssuerURL: "http://localhost:4444/", diff --git a/internal/route/logout.go b/internal/route/logout.go index edd59c0..c159a6e 100644 --- a/internal/route/logout.go +++ b/internal/route/logout.go @@ -3,6 +3,9 @@ package route import ( "net/http" + "forge.cadoles.com/Cadoles/daddy/internal/session" + "github.com/pkg/errors" + "forge.cadoles.com/Cadoles/daddy/internal/config" oidc "forge.cadoles.com/wpetit/goweb-oidc" "gitlab.com/wpetit/goweb/logger" @@ -21,6 +24,10 @@ func handleLogout(w http.ResponseWriter, r *http.Request) { logger.F("postLogoutURL", conf.OIDC.PostLogoutRedirectURL), ) + if err := session.ClearUserEmail(w, r, false); err != nil { + panic(errors.WithStack(err)) + } + client.Logout(w, r, conf.OIDC.PostLogoutRedirectURL) } diff --git a/internal/route/mount.go b/internal/route/mount.go index 860def4..973424d 100644 --- a/internal/route/mount.go +++ b/internal/route/mount.go @@ -1,15 +1,22 @@ package route import ( + "net/http" + "time" + "forge.cadoles.com/Cadoles/daddy/internal/config" "forge.cadoles.com/Cadoles/daddy/internal/graph" "forge.cadoles.com/Cadoles/daddy/internal/graph/generated" "forge.cadoles.com/Cadoles/daddy/internal/session" oidc "forge.cadoles.com/wpetit/goweb-oidc" "github.com/99designs/gqlgen/graphql/handler" + "github.com/99designs/gqlgen/graphql/handler/extension" + "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/99designs/gqlgen/graphql/playground" + "github.com/gorilla/websocket" "github.com/go-chi/chi" + "github.com/rs/cors" "gitlab.com/wpetit/goweb/static" ) @@ -21,20 +28,39 @@ func Mount(r *chi.Mux, config *config.Config) error { r.Get("/logout/redirect", handleLogoutRedirect) r.Route("/api", func(r chi.Router) { + r.Use(cors.New(cors.Options{ + AllowedOrigins: config.HTTP.CORS.AllowedOrigins, + AllowCredentials: config.HTTP.CORS.AllowCredentials, + Debug: config.Debug, + }).Handler) r.Use(oidc.Middleware) r.Use(session.UserEmailMiddleware) - gql := handler.NewDefaultServer( + gql := handler.New( generated.NewExecutableSchema(generated.Config{ Resolvers: &graph.Resolver{}, }), ) + gql.AddTransport(transport.POST{}) + gql.AddTransport(&transport.Websocket{ + KeepAlivePingInterval: 10 * time.Second, + Upgrader: websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + // TODO Check WS connection origin + return true + }, + ReadBufferSize: 1024, + WriteBufferSize: 1024, + }, + }) + if config.Debug { - r.Get("/v1/graphql", playground.Handler("GraphQL playground", "/api/v1/graphql")) + gql.Use(extension.Introspection{}) + r.Get("/v1/playground", playground.Handler("GraphQL playground", "/api/v1/graphql")) } - r.Post("/v1/graphql", gql.ServeHTTP) + r.Handle("/v1/graphql", gql) }) notFoundHandler := r.NotFoundHandler() diff --git a/internal/session/middleware.go b/internal/session/user_email.go similarity index 86% rename from internal/session/middleware.go rename to internal/session/user_email.go index 6120bc9..b1dc0c3 100644 --- a/internal/session/middleware.go +++ b/internal/session/user_email.go @@ -61,6 +61,23 @@ func SaveUserEmail(w http.ResponseWriter, r *http.Request, email string) error { return nil } +func ClearUserEmail(w http.ResponseWriter, r *http.Request, saveSession bool) error { + sess, err := getSession(w, r) + if err != nil { + return errors.WithStack(err) + } + + sess.Unset(string(userEmailKey)) + + if saveSession { + if err := sess.Save(w, r); err != nil { + return errors.WithStack(err) + } + } + + return nil +} + func GetUserEmail(w http.ResponseWriter, r *http.Request) (string, error) { sess, err := getSession(w, r) if err != nil {