diff --git a/Makefile b/Makefile index 20566c0..cf6a758 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ down: docker-compose down -v --remove-orphans db-shell: - docker-compose exec postgres psql -Usupergraph + docker-compose exec postgres psql -Udaddy hydra-shell: docker-compose exec hydra /bin/sh \ No newline at end of file diff --git a/README.md b/README.md index 8620f09..43e9f35 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,15 @@ Les services suivants devraient être disponibles après démarrage de l'environ |Service|Type|Accès|Description| |-------|----|-----|-----------| |Application React|HTTP (UI)|http://localhost:8081/|Page d'accueil de l'application React (serveur Webpack)| -|Interface Web GraphQL|HTTP (UI)|http://localhost:8080/|Interface Web de développement de l'API GraphQL| +|Interface Web GraphQL|HTTP (UI)|http://localhost:8080/|Interface Web de développement de l'API GraphQL **\***| |Serveur GraphQL|HTTP (GraphQL)|http://localhost:8080/api/v1/graphql|Point d'entrée de l'API GraphQL| |Serveur Hydra|HTTP (ReST)|http://localhost:4444|Point d'entrée pour l'API OAuth2 d'[Hydra](https://www.ory.sh/hydra/docs/)| |Serveur Hydra Passwordless|HTTP|http://localhost:3000|Point d'entrée pour la ["Login/Consent App"](https://www.ory.sh/hydra/docs/implementing-consent) [hydra-passwordless](https://forge.cadoles.com/wpetit/hydra-passwordless)| |Serveur FakeSMTP|HTTP|http://localhost:8082|Interface web du serveur [FakeSMTP](https://forge.cadoles.com/wpetit/fake-smtp) |Serveur PostgreSQL|TCP/IP (PostgreSQL)|`127.0.0.1:5432`|Port de connexion à la base de données PostgreSQL de développement| +**\*** Pensez à passer l'attribut `auth_fail_block: false` dans le fichier `backend/config/dev.yml` si vous voulez pouvoir utiliser cette interface sans avoir à définir l'entête `Authorization`. + #### Fichiers/répertoires notables |Chemin|Description| diff --git a/backend/config/allow.list b/backend/config/allow.list index e69de29..92cf2d2 100644 --- a/backend/config/allow.list +++ b/backend/config/allow.list @@ -0,0 +1,18 @@ +/* fetchUser */ + +variables { + "email": "" +} + + + query fetchUser { + user(where: {email: {eq: $email}}) { + id + created_at + updated_at + email, + full_name + } + } + + diff --git a/backend/config/dev.yml b/backend/config/dev.yml index 185f4ce..a32f542 100644 --- a/backend/config/dev.yml +++ b/backend/config/dev.yml @@ -1,9 +1,9 @@ -app_name: "Test Development" +app_name: "Daddy Dev" host_port: 0.0.0.0:8080 web_ui: true # debug, error, warn, info -log_level: "info" +log_level: debug # enable or disable http compression (uses gzip) http_compress: true @@ -15,7 +15,7 @@ http_compress: true production: false # Throw a 401 on auth failure for queries that need auth -auth_fail_block: false +auth_fail_block: true # Latency tracing for database queries and remote joins # the resulting latency information is returned with the @@ -42,6 +42,8 @@ secret_key: supercalifajalistics # An origin may contain a wildcard (*) to replace 0 or more # characters (i.e.: http://*.domain.com). cors_allowed_origins: ["*"] +cors_allowed_headers: ["Authorization", "Content-Type", "Mode"] +cors_allowed_methods: ["POST"] # Debug Cross Origin Resource Sharing requests cors_debug: false @@ -65,18 +67,16 @@ cors_debug: false auth: # Can be 'rails', 'jwt' or 'header' type: jwt - cookie: _supergraph_session + #cookie: _supergraph_session # Comment this out if you want to disable setting # the user_id via a header for testing. # Disable in production - creds_in_header: true + #creds_in_header: false - # jwt: - # provider: auth0 - # secret: abc335bfcfdb04e50db5bb0a4d67ab9 - # public_key_file: /secrets/public_key.pem - # public_key_type: ecdsa #rsa + jwt: + provider: hydra + jwks_url: http://hydra:4444/.well-known/jwks.json # header: # name: dnt @@ -87,16 +87,16 @@ auth: # In this example actions using this auth can only be # called from the Google Appengine Cron service that # sets a special header to all it's requests -auths: - - name: from_taskqueue - type: header - header: - name: X-Appengine-Cron - exists: true +# auths: + # - name: from_taskqueue + # type: header + # header: + # name: X-Appengine-Cron + # exists: true database: type: postgres - host: db + host: localhost port: 5432 dbname: daddy user: daddy @@ -105,19 +105,19 @@ database: #schema: "public" #pool_size: 10 #max_retries: 0 - #log_level: "debug" + log_level: "debug" # Set session variable "user.id" to the user id # Enable this if you need the user id in triggers, etc - set_user_id: false + set_user_id: true # database ping timeout is used for db health checking ping_timeout: 1m # Define additional variables here to be used with filters variables: - #admin_account_id: "5" - admin_account_id: "sql:select id from users where admin = true limit 1" + # admin_account_id: "5" + # admin_account_id: "sql:select id from users where admin = true limit 1" # Field and table names that you wish to block @@ -135,67 +135,89 @@ database: # which in this case refreshes a materialized view in the database. # The auth_name is from one of the configured auths actions: - - name: refresh_leaderboard_users - sql: REFRESH MATERIALIZED VIEW CONCURRENTLY "leaderboard_users" - auth_name: from_taskqueue + # - name: refresh_leaderboard_users + # sql: REFRESH MATERIALIZED VIEW CONCURRENTLY "leaderboard_users" + # auth_name: from_taskqueue tables: - - name: customers - remotes: - - name: payments - id: stripe_id - url: http://rails_app:3000/stripe/$id - path: data - # debug: true - pass_headers: - - cookie - set_headers: - - name: Host - value: 0.0.0.0 + # - name: customers + # remotes: + # - name: payments + # id: stripe_id + # url: http://rails_app:3000/stripe/$id + # path: data + # # debug: true + # pass_headers: + # - cookie + # set_headers: + # - name: Host + # value: 0.0.0.0 # - name: Authorization # value: Bearer - - # You can create new fields that have a - # real db table backing them - name: me - table: users + # - # You can create new fields that have a + # # real db table backing them + # name: me + # table: users -#roles_query: "SELECT * FROM users WHERE id = $user_id" +roles_query: "select * from users where users.email = $user_id" roles: + # Rôle par défaut si l'utilisateur n'existe pas dans la table `users` - name: anon tables: - name: users - query: - limit: 10 + # insert: + # block: true + # query: + # block: true + # update: + # block: true + # delete: + # block: true + # Rôle par défaut si l'utilisateur existe dans la table `users` + # mais que la valeur de la colonne `role` n'est pas définie - name: user tables: - name: users - query: - filters: ["{ id: { _eq: $user_id } }"] - - - name: products - query: - limit: 50 - filters: ["{ user_id: { eq: $user_id } }"] - disable_functions: false - insert: - filters: ["{ user_id: { eq: $user_id } }"] - presets: - - user_id: "$user_id" - - created_at: "now" - + block: true + query: + filters: ["{ email: { _eq: $user_id } }"] update: - filters: ["{ user_id: { eq: $user_id } }"] - presets: - - updated_at: "now" - + columns: + - full_name + filters: ["{ email: { _eq: $user_id } }"] delete: block: true + - name: admin + match: role = 'admin' + tables: + - name: users + + # - name: products + # query: + # limit: 50 + # filters: ["{ user_id: { eq: $user_id } }"] + # disable_functions: false + + # insert: + # filters: ["{ user_id: { eq: $user_id } }"] + # presets: + # - user_id: "$user_id" + # - created_at: "now" + + # update: + # filters: ["{ user_id: { eq: $user_id } }"] + # presets: + # - updated_at: "now" + + # delete: + # block: true + # - name: admin # match: id = 1000 # tables: diff --git a/backend/config/migrations/0_init.sql b/backend/config/migrations/0_init.sql index 17d993c..6db6d3c 100644 --- a/backend/config/migrations/0_init.sql +++ b/backend/config/migrations/0_init.sql @@ -5,7 +5,8 @@ CREATE TABLE public.users ( full_name text, email text UNIQUE NOT NULL CHECK (length(email) < 255), created_at timestamptz NOT NULL NOT NULL DEFAULT NOW(), - updated_at timestamptz NOT NULL NOT NULL DEFAULT NOW() + updated_at timestamptz NOT NULL NOT NULL DEFAULT NOW(), + role varchar(64) ); ---- create above / drop below ---- @@ -14,4 +15,3 @@ CREATE TABLE public.users ( -- then delete the separator line above. DROP TABLE public.users - diff --git a/backend/config/seed.js b/backend/config/seed.js index e027af3..f016d11 100644 --- a/backend/config/seed.js +++ b/backend/config/seed.js @@ -1,19 +1,33 @@ -// Example script to seed database +// Voir https://supergraph.dev/docs/seed -var users = []; +var users = [ + { + full_name: 'Admin', + email: 'admin@cadoles.com', + role: 'admin', + }, + { + full_name: 'User 1', + email: 'user1@cadoles.com', + role: 'user', + }, + { + full_name: 'User 2', + email: 'user2@cadoles.com', + role: 'user', + }, + { + full_name: 'User 3', + email: 'user3@cadoles.com', + role: 'user', + } +]; -for (i = 0; i < 10; i++) { - var data = { - full_name: fake.name(), - email: fake.email() - } - - var res = graphql(" \ +for (var user, i = 0; (user = users[i]); i++) { + var res = graphql(" \ mutation { \ user(insert: $data) { \ id \ } \ - }", { data: data }) - - users.push(res.user) + }", { data: user }); } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1c75906..873279d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,7 @@ services: HYDRA_ADMIN_URL: http://localhost:4445 SERVE_PUBLIC_CORS_ENABLED: "true" SERVE_PUBLIC_CORS_ALLOWED_ORIGINS: http://localhost:8081 + WEBFINGER_JWKS_BROADCAST_KEYS: hydra.openid.id-token,hydra.jwt.access-token ports: - 4444:4444 command: hydra serve all --dangerous-force-http diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5979b02..1e597da 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1093,6 +1093,49 @@ "integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, "@redux-saga/core": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.1.3.tgz", @@ -1585,6 +1628,24 @@ } } }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, "ajv": { "version": "6.12.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", @@ -3272,6 +3333,12 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "clean-webpack-plugin": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", @@ -3542,6 +3609,258 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "copy-webpack-plugin": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.0.2.tgz", + "integrity": "sha512-9Gm8X0c6eXlKnmltMPFCBeGOKjtcRIyTt4VaO3k1TkNgVTe5Ov2lYsYVuyLp0kp8DItO3apewflM+1GYgh6V2Q==", + "dev": true, + "requires": { + "cacache": "^15.0.4", + "fast-glob": "^3.2.2", + "find-cache-dir": "^3.3.1", + "glob-parent": "^5.1.1", + "globby": "^11.0.1", + "loader-utils": "^2.0.0", + "normalize-path": "^3.0.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.7.0", + "serialize-javascript": "^3.1.0", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "cacache": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.4.tgz", + "integrity": "sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "tar": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.2.tgz", + "integrity": "sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.0", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, "core-js": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", @@ -3965,6 +4284,23 @@ } } }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -4622,6 +4958,74 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "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", @@ -4634,6 +5038,15 @@ "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", "dev": true }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -4850,6 +5263,15 @@ "readable-stream": "^2.0.0" } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -5077,6 +5499,11 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, + "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==" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -5498,6 +5925,12 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -6208,6 +6641,12 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -6340,6 +6779,68 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz", + "integrity": "sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", + "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -8015,6 +8516,12 @@ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rework": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", @@ -8058,6 +8565,12 @@ "inherits": "^2.0.1" } }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index f744221..6f0d883 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "@types/uuid": "^7.0.3", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^6.0.2", "css-loader": "^1.0.1", "extract-loader": "^3.1.0", "file-loader": "^2.0.0", @@ -53,6 +54,7 @@ "@types/qs": "^6.9.3", "bulma": "^0.7.2", "bulma-switch": "^2.0.0", + "graphql-request": "^2.0.0", "jwt-decode": "^2.2.0", "qs": "^6.9.4", "react": "^16.12.0", diff --git a/frontend/src/components/HomePage/HomePage.tsx b/frontend/src/components/HomePage/HomePage.tsx index 150726a..b9ac584 100644 --- a/frontend/src/components/HomePage/HomePage.tsx +++ b/frontend/src/components/HomePage/HomePage.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { Page } from '../Page'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; import { RootState } from '../../store/reducers/root'; export function HomePage() { @@ -14,8 +14,8 @@ export function HomePage() {
{ - currentUser ? -

Bonjour {currentUser.email} !

: + currentUser && currentUser.full_name ? +

Bonjour {currentUser.full_name} !

:

Veuillez vous authentifier.

}
diff --git a/frontend/src/config.ts b/frontend/src/config.ts index 9b59e24..98ee0a4 100644 --- a/frontend/src/config.ts +++ b/frontend/src/config.ts @@ -7,7 +7,8 @@ export const Config = { oauth2AuthorizeURL: get("oauth2AuthorizeURL", "http://localhost:4444/oauth2/auth"), oauth2TokenURL: get("oauth2TokenURL", "http://localhost:4444/oauth2/token"), oauth2LogoutURL: get("oauth2LogoutURL", "http://localhost:4444/oauth2/sessions/logout"), - oauth2PostLogoutRedirectURI: get("oauth2PostLogoutRedirectURI", "http://localhost:8081") + oauth2PostLogoutRedirectURI: get("oauth2PostLogoutRedirectURI", "http://localhost:8081"), + graphQLEndpoint: get("graphQLEndpoint", "http://localhost:8080/api/v1/graphql") }; function get(key: string, defaultValue: T):T { diff --git a/frontend/src/resources/config.sample.js b/frontend/src/resources/config.sample.js new file mode 100644 index 0000000..8b9dd81 --- /dev/null +++ b/frontend/src/resources/config.sample.js @@ -0,0 +1,3 @@ +// Use this file to customize client configuration. +// See frontend/src/config.ts for more informations. +window['__CONFIG__'] = {}; \ No newline at end of file diff --git a/frontend/src/store/actions/profile.ts b/frontend/src/store/actions/profile.ts new file mode 100644 index 0000000..c46a75f --- /dev/null +++ b/frontend/src/store/actions/profile.ts @@ -0,0 +1,19 @@ +import { Action } from "redux"; +import { User } from "../../types/user"; + +export const FETCH_PROFILE_REQUEST = 'FETCH_PROFILE_REQUEST'; +export const FETCH_PROFILE_SUCCESS = 'FETCH_PROFILE_SUCCESS'; +export const FETCH_PROFILE_FAILURE = 'FETCH_PROFILE_FAILURE'; + +export interface fetchProfileRequestAction extends Action { + +} + +export interface fetchProfileSuccessAction extends Action { + profile: User +} + + +export function fetchProfile(): fetchProfileRequestAction { + return { type: FETCH_PROFILE_REQUEST } +} \ No newline at end of file diff --git a/frontend/src/store/reducers/auth.ts b/frontend/src/store/reducers/auth.ts index a0bd507..e888748 100644 --- a/frontend/src/store/reducers/auth.ts +++ b/frontend/src/store/reducers/auth.ts @@ -1,6 +1,7 @@ import { Action } from "redux"; import { User } from "../../types/user"; import { SET_CURRENT_USER, setCurrentUserAction, LOGOUT } from "../actions/auth"; +import { FETCH_PROFILE_SUCCESS, fetchProfileSuccessAction } from "../actions/profile"; export interface AuthState { isAuthenticated: boolean @@ -18,6 +19,9 @@ export function authReducer(state = defaultState, action: Action): AuthState { return handleSetCurrentUser(state, action as setCurrentUserAction); case LOGOUT: return handleLogout(state); + case FETCH_PROFILE_SUCCESS: + return handleFetchProfileSuccess(state, action as fetchProfileSuccessAction); + } return state; } @@ -38,4 +42,14 @@ function handleLogout(state: AuthState): AuthState { isAuthenticated: false, currentUser: null, }; -} \ No newline at end of file +}; + +function handleFetchProfileSuccess(state: AuthState, { profile }: fetchProfileSuccessAction): AuthState { + return { + ...state, + isAuthenticated: true, + currentUser: { + ...profile, + } + }; +}; \ No newline at end of file diff --git a/frontend/src/store/sagas/init.ts b/frontend/src/store/sagas/init.ts new file mode 100644 index 0000000..da4d595 --- /dev/null +++ b/frontend/src/store/sagas/init.ts @@ -0,0 +1,18 @@ +import { all, put } from "redux-saga/effects"; +import { getSavedAccessGrant } from "../../util/auth"; +import { parseIdToken } from "../actions/auth"; + +export function* initRootSaga() { + yield all([ + retrieveSessionSaga(), + ]); +} + +export function* retrieveSessionSaga() { + console.log("Checking session status..."); + + const accessGrant = getSavedAccessGrant(); + if (!accessGrant) return; + + yield put(parseIdToken(accessGrant.id_token)); +} \ No newline at end of file diff --git a/frontend/src/store/sagas/root.ts b/frontend/src/store/sagas/root.ts index dfa853a..71fdf80 100644 --- a/frontend/src/store/sagas/root.ts +++ b/frontend/src/store/sagas/root.ts @@ -1,10 +1,14 @@ import { all } from 'redux-saga/effects'; import { failureRootSaga } from './failure'; import { authRootSaga } from './auth'; +import { initRootSaga } from './init'; +import { usersRootSaga } from './users'; export function* rootSaga() { yield all([ + initRootSaga(), failureRootSaga(), authRootSaga(), + usersRootSaga(), ]); } diff --git a/frontend/src/store/sagas/users.ts b/frontend/src/store/sagas/users.ts new file mode 100644 index 0000000..40521b3 --- /dev/null +++ b/frontend/src/store/sagas/users.ts @@ -0,0 +1,37 @@ +import { DaddyClient } from "../../util/daddy"; +import { Config } from "../../config"; +import { getSavedAccessGrant } from "../../util/auth"; +import { all, takeLatest, put, select } from "redux-saga/effects"; +import { FETCH_PROFILE_REQUEST, fetchProfile, FETCH_PROFILE_FAILURE, FETCH_PROFILE_SUCCESS } from "../actions/profile"; +import { SET_CURRENT_USER } from "../actions/auth"; +import { RootState } from "../reducers/root"; +import { User } from "../../types/user"; + +export function* usersRootSaga() { + yield all([ + takeLatest(SET_CURRENT_USER, onCurrentUserChangeSaga), + takeLatest(FETCH_PROFILE_REQUEST, fetchProfileSaga), + ]); +} + +export function* onCurrentUserChangeSaga() { + yield put(fetchProfile()); +} + + + +export function* fetchProfileSaga() { + const grant = getSavedAccessGrant(); + const client = new DaddyClient(Config.graphQLEndpoint, grant.id_token); + + let profile: User; + try { + const currentUser: User = yield select((state: RootState) => state.auth.currentUser); + profile = yield client.fetchUser(currentUser.email).then(result => result.user); + } catch(err) { + yield put({ type: FETCH_PROFILE_FAILURE, err }); + return; + } + + yield put({type: FETCH_PROFILE_SUCCESS, profile }); +} \ No newline at end of file diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index 70d7e8b..13c920d 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -1,3 +1,6 @@ export interface User { email: string + full_name?: string + updated_at?: Date + created_at?: Date } \ No newline at end of file diff --git a/frontend/src/util/daddy.ts b/frontend/src/util/daddy.ts index 22bb4c5..953ea9d 100644 --- a/frontend/src/util/daddy.ts +++ b/frontend/src/util/daddy.ts @@ -1,3 +1,6 @@ +import { GraphQLClient } from 'graphql-request' +import { Config } from "../config"; + export class UnauthorizedError extends Error { constructor(...args: any[]) { super(...args) @@ -7,16 +10,35 @@ export class UnauthorizedError extends Error { export class DaddyClient { - assertOk(res: any) { - if (!res.ok) return Promise.reject(new Error('Request failed')); - return res; + gql: GraphQLClient + + constructor(endpoint: string, idToken: string) { + this.gql = new GraphQLClient(endpoint, { + headers: { + Authorization: `Bearer ${idToken}`, + mode: 'cors', + } + }); } - assertAuthorization(res: any) { - if (res.status === 401 || res.status === 404) return Promise.reject(new UnauthorizedError()); - return res; + fetchUser(email: string) { + return this.gql.rawRequest(` + query fetchUser { + user(where: {email: {eq: $email}}) { + id + created_at + updated_at + email, + full_name + } + } + `, { email }) + .then(this.assertAuthorization) } -} + assertAuthorization({ status, data }: any) { + if (status === 401) return Promise.reject(new UnauthorizedError()); + return data; + } -export const daddy = new DaddyClient(); \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index ce20669..f552187 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -4,6 +4,7 @@ const path = require('path'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); const env = process.env; @@ -72,5 +73,10 @@ module.exports = { inject: false, favicon: "./src/resources/favicon.png", }), + new CopyPlugin({ + patterns: [ + { from: './src/resources/config.sample.js', to: 'config.js' }, + ], + }), ] } \ No newline at end of file diff --git a/misc/containers/postgres/initdb.d/init-databases.sh b/misc/containers/postgres/initdb.d/init-databases.sh index ddbbb85..9831c3a 100644 --- a/misc/containers/postgres/initdb.d/init-databases.sh +++ b/misc/containers/postgres/initdb.d/init-databases.sh @@ -12,4 +12,5 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-E CREATE USER daddy WITH ENCRYPTED PASSWORD 'daddy'; CREATE DATABASE daddy; GRANT ALL PRIVILEGES ON DATABASE daddy TO daddy; + ALTER DATABASE daddy OWNER TO daddy; EOSQL \ No newline at end of file diff --git a/misc/containers/super-graph/Dockerfile b/misc/containers/super-graph/Dockerfile index 2e2ce94..dfca8ca 100644 --- a/misc/containers/super-graph/Dockerfile +++ b/misc/containers/super-graph/Dockerfile @@ -5,12 +5,12 @@ ARG HTTPS_PROXY= ARG http_proxy= ARG https_proxy= -ARG SUPERGRAPH_VERSION=v0.14.17 +ARG SUPERGRAPH_VERSION=88ba105b70c60b2c7467dc1f76f041cec2614a04 ARG WAITFORIT_VERSION=v2.4.1 RUN apk add --no-cache go make git curl bash ca-certificates -RUN git clone https://github.com/dosco/super-graph \ +RUN git clone https://forge.cadoles.com/wpetit/super-graph.git \ && export PATH="$PATH:/root/go/bin" \ && export CGO_ENABLED=0 \ && cd super-graph \