diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..5b2bf693 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,24 @@ +name: Release + +on: + push: + branches: + - main + - beta + +jobs: + release: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - run: yarn install + - name: Build + run: yarn run build + - name: Release + run: yarn workspaces run semantic-release -e semantic-release-monorepo diff --git a/.releaserc.json b/.releaserc.json deleted file mode 100644 index 342a9a94..00000000 --- a/.releaserc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "semantic-release-monorepo", - "branches": [ - "+([0-9])?(.{+([0-9]),x}).x", - "main", - "next", - "next-major", - {"name": "beta", "prerelease": true}, - {"name": "alpha", "prerelease": true} - ], - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - "@semantic-release/changelog", - "@semantic-release/npm", - "@semantic-release/github", - "@semantic-release/git" - ] -} diff --git a/cache.patch b/cache.patch new file mode 100644 index 00000000..73b6b78c --- /dev/null +++ b/cache.patch @@ -0,0 +1,27 @@ +diff --git a/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js b/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js +index edc673f..cbd390c 100644 +--- a/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js ++++ b/node_modules/gatsby/cache-dir/create-content-digest-browser-shim.js +@@ -1 +1 @@ +-exports.createContentDigest = () => `` ++export const createContentDigest = () => `` +diff --git a/node_modules/gatsby/cache-dir/public-page-renderer.js b/node_modules/gatsby/cache-dir/public-page-renderer.js +index 6a91ada..e2ee03a 100644 +--- a/node_modules/gatsby/cache-dir/public-page-renderer.js ++++ b/node_modules/gatsby/cache-dir/public-page-renderer.js +@@ -1,9 +1,11 @@ + const preferDefault = m => (m && m.default) || m + ++let renderer = () => null; ++ + if (process.env.BUILD_STAGE === `develop`) { +- module.exports = preferDefault(require(`./public-page-renderer-dev`)) ++ renderer = preferDefault(require(`./public-page-renderer-dev`)) + } else if (process.env.BUILD_STAGE === `build-javascript`) { +- module.exports = preferDefault(require(`./public-page-renderer-prod`)) +-} else { +- module.exports = () => null ++ renderer = preferDefault(require(`./public-page-renderer-prod`)) + } ++ ++export default renderer; diff --git a/examples/my-gatsby-site/.env b/examples/my-gatsby-site/.env index 2c5b3ada..b52c1ac0 100644 --- a/examples/my-gatsby-site/.env +++ b/examples/my-gatsby-site/.env @@ -1,2 +1,2 @@ -GATSBY_LENS_API_URL=https://api.photonq.lens.atsnek.com/graphql +GATSBY_LENS_API_URL=https://api.lens.walther.exp.univie.ac.at:8080/graphql #SENTRY_AUTH_TOKEN=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 diff --git a/examples/my-gatsby-site/package.json b/examples/my-gatsby-site/package.json index f0754262..7b21d84d 100644 --- a/examples/my-gatsby-site/package.json +++ b/examples/my-gatsby-site/package.json @@ -10,18 +10,17 @@ "scripts": { "develop": "gatsby develop", "start": "gatsby develop", - "build": "NODE_OPTIONS=--max-old-space-size=8192 gatsby build", "serve": "gatsby serve", "clean": "gatsby clean", "typecheck": "tsc --noEmit" }, "dependencies": { - "@atsnek/jaen": "^1.0.0-rc.1", - "@atsnek/jaen-fields-mdx": "^1.0.0-rc.1", + "jaen": "*", + "jaen-fields-mdx": "*", "@radix-ui/react-icons": "^1.3.0", "@react-icons/all-files": "https://github.com/react-icons/react-icons/releases/download/v4.11.0/react-icons-all-files-4.11.0.tgz", "gatsby": "^5.11.0", - "gatsby-plugin-jaen": "^1.0.0-rc.1", + "gatsby-plugin-jaen": "*", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/examples/my-gatsby-site/src/gatsby-plugin-jaen/components/Layout.tsx b/examples/my-gatsby-site/src/gatsby-plugin-jaen/components/Layout.tsx index 7eba9aec..f89311c7 100644 --- a/examples/my-gatsby-site/src/gatsby-plugin-jaen/components/Layout.tsx +++ b/examples/my-gatsby-site/src/gatsby-plugin-jaen/components/Layout.tsx @@ -1,4 +1,4 @@ -import {LayoutProps, useWidget} from '@atsnek/jaen' +import {LayoutProps, useWidget} from 'jaen' import {Box, Heading, HStack, Button} from '@chakra-ui/react' import {useEffect} from 'react' diff --git a/examples/my-gatsby-site/src/images/icon.png b/examples/my-gatsby-site/src/images/icon.png deleted file mode 100644 index 38b2fb0e..00000000 Binary files a/examples/my-gatsby-site/src/images/icon.png and /dev/null differ diff --git a/examples/my-gatsby-site/src/pages/404.tsx b/examples/my-gatsby-site/src/pages/404.tsx index 31ab8d72..aeeb0bea 100644 --- a/examples/my-gatsby-site/src/pages/404.tsx +++ b/examples/my-gatsby-site/src/pages/404.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import {Link, HeadFC, PageProps} from 'gatsby' -import {PageConfig} from '@atsnek/jaen' +import {PageConfig} from 'jaen' import {LightMode, GlobalStyle} from '@chakra-ui/react' const pageStyles = { @@ -55,7 +55,7 @@ const NotFoundPage: React.FC = () => { export default NotFoundPage -export {Head} from '@atsnek/jaen' +export {Head} from 'jaen' export const pageConfig: PageConfig = { label: 'Oops! Page not found', diff --git a/examples/my-gatsby-site/src/pages/contact.tsx b/examples/my-gatsby-site/src/pages/contact.tsx index 99df281d..07723834 100644 --- a/examples/my-gatsby-site/src/pages/contact.tsx +++ b/examples/my-gatsby-site/src/pages/contact.tsx @@ -1,4 +1,4 @@ -import {PageConfig, PageProps, useNotificationsContext} from '@atsnek/jaen' +import {PageConfig, PageProps, useNotificationsContext} from 'jaen' import { Button, diff --git a/examples/my-gatsby-site/src/pages/editor.tsx b/examples/my-gatsby-site/src/pages/editor.tsx index 161a2fad..225326e8 100644 --- a/examples/my-gatsby-site/src/pages/editor.tsx +++ b/examples/my-gatsby-site/src/pages/editor.tsx @@ -1,4 +1,4 @@ -import {Field, PageConfig, PageProps} from '@atsnek/jaen' +import {Field, PageConfig, PageProps} from 'jaen' const Page: React.FC = ({location, pageContext}) => { return diff --git a/examples/my-gatsby-site/src/pages/hidden-node.tsx b/examples/my-gatsby-site/src/pages/hidden-node.tsx index 0a714c3e..f754d7bd 100644 --- a/examples/my-gatsby-site/src/pages/hidden-node.tsx +++ b/examples/my-gatsby-site/src/pages/hidden-node.tsx @@ -1,4 +1,4 @@ -import {PageConfig, PageProps} from '@atsnek/jaen' +import {PageConfig, PageProps} from 'jaen' const Page: React.FC = ({location, pageContext}) => { return ( diff --git a/examples/my-gatsby-site/src/pages/index.tsx b/examples/my-gatsby-site/src/pages/index.tsx index 1eb8cdc2..46e7a2d4 100644 --- a/examples/my-gatsby-site/src/pages/index.tsx +++ b/examples/my-gatsby-site/src/pages/index.tsx @@ -10,14 +10,14 @@ import { useMediaModal, usePageContext, useSiteMetadataContext -} from '@atsnek/jaen' +} from 'jaen' import {Link, useJaenFrameMenuContext} from 'gatsby-plugin-jaen' import {Box, Button, LightMode, Text} from '@chakra-ui/react' import {graphql} from 'gatsby' import * as React from 'react' -import {UncontrolledMdxField} from '@atsnek/jaen-fields-mdx' +import {UncontrolledMdxField} from 'jaen-fields-mdx' import {FaCogs} from '@react-icons/all-files/fa/FaCogs' const pageStyles = { @@ -403,4 +403,4 @@ export const query = graphql` } ` -export {Head} from '@atsnek/jaen' +export {Head} from 'jaen' diff --git a/examples/my-gatsby-site/src/pages/mdx.tsx b/examples/my-gatsby-site/src/pages/mdx.tsx index 041289ad..02de2874 100644 --- a/examples/my-gatsby-site/src/pages/mdx.tsx +++ b/examples/my-gatsby-site/src/pages/mdx.tsx @@ -1,8 +1,9 @@ -import {Field, PageConfig, PageProps} from '@atsnek/jaen' +import {Field, PageConfig, PageProps} from 'jaen' import {Box} from '@chakra-ui/react' -import {MdxField} from '@atsnek/jaen-fields-mdx' +import {MdxField, UncontrolledMdxField} from 'jaen-fields-mdx' import {Link} from 'gatsby-plugin-jaen' -import {usePage} from '@atsnek/jaen' +import {usePage} from 'jaen' +import {useState} from 'react' const QASMPlayground: React.FC<{ playground?: boolean @@ -26,6 +27,17 @@ const Page: React.FC = ({location, pageContext}) => { console.log('page:', page) + const [value, setValue] = useState() + + return ( + + ) + return ( <> = ({location, pageContext}) => { diff --git a/examples/my-gatsby-site/src/pages/user/[...].tsx b/examples/my-gatsby-site/src/pages/user/[...].tsx index 7f60711e..19c7f79b 100644 --- a/examples/my-gatsby-site/src/pages/user/[...].tsx +++ b/examples/my-gatsby-site/src/pages/user/[...].tsx @@ -1,4 +1,4 @@ -import {PageConfig, PageProps} from '@atsnek/jaen' +import {PageConfig, PageProps} from 'jaen' const Page: React.FC = ({location, pageContext}) => { // everything after /user/ is the handle diff --git a/examples/my-gatsby-site/src/pages/wholesale.tsx b/examples/my-gatsby-site/src/pages/wholesale.tsx index a8c80c41..7cf7a9e4 100644 --- a/examples/my-gatsby-site/src/pages/wholesale.tsx +++ b/examples/my-gatsby-site/src/pages/wholesale.tsx @@ -1,4 +1,4 @@ -import {PageConfig, PageProps} from '@atsnek/jaen' +import {PageConfig, PageProps} from 'jaen' const Page: React.FC = ({location, pageContext}) => { return ( @@ -15,6 +15,6 @@ export const pageConfig: PageConfig = { icon: 'FaWarehouse', auth: { isRequired: true, - roles: ['4d84a68f-7b18-4efe-ae73-d6d3dd226110'] + roles: ['260237544631828483:kassabuch:admin'] } } diff --git a/examples/my-gatsby-site/src/templates/BlogPage.tsx b/examples/my-gatsby-site/src/templates/BlogPage.tsx index 26a735ce..6bff1b0e 100644 --- a/examples/my-gatsby-site/src/templates/BlogPage.tsx +++ b/examples/my-gatsby-site/src/templates/BlogPage.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import {Link, HeadFC, PageProps, navigate, graphql} from 'gatsby' -import {Field, PageConfig, useField, useJaenPageIndex} from '@atsnek/jaen' +import {Field, PageConfig, useField, useJaenPageIndex} from 'jaen' import {Button} from '@chakra-ui/react' -import {MdxField} from '@atsnek/jaen-fields-mdx' +import {MdxField} from 'jaen-fields-mdx' const BlogPage: React.FC = props => { const index = useJaenPageIndex() @@ -67,4 +67,4 @@ export const query = graphql` } ` -export {Head} from '@atsnek/jaen' +export {Head} from 'jaen' diff --git a/lerna.json b/lerna.json deleted file mode 100644 index 89c5be33..00000000 --- a/lerna.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "version": "independent", - "npmClient": "yarn", - "useWorkspaces": true -} diff --git a/package.json b/package.json index 8c5f017a..8d4f86a8 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "description": "The Webapps framework for creating scalable and dynamic applications with ease.", "private": true, "version": "1.0.0", - "repository": "https://github.com/atsnek/jaen", - "author": "Nico Schett ", + "repository": "https://github.com/jaenjs/jaen", + "author": "Nico Schett ", "scripts": { "lint:fix": "yarn eslint packages/jaen/src/ --fix", "prettier:fix": "yarn prettier packages/jaen/src/ --write", - "format": "yarn prettier:fix && yarn lint:fix" + "format": "yarn prettier:fix && yarn lint:fix", + "build": "yarn workspace jaen run build && yarn workspace gatsby-source-jaen run build && yarn workspace gatsby-plugin-jaen run build && yarn workspace gatsby-jaen-mailpress run build && yarn workspace gatsby-jaen-lens run build && yarn workspace jaen-fields-mdx run build" }, "workspaces": [ "packages/jaen", @@ -25,6 +26,9 @@ "prettier-plugin-organize-imports": "^3.2.2" }, "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.0", + "@semantic-release/release-notes-generator": "^14.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0", "eslint": "^8.0.1", "eslint-config-standard-with-typescript": "^27.0.1", @@ -32,6 +36,8 @@ "eslint-plugin-n": "^15.0.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "^6.0.0", - "eslint-plugin-react": "^7.32.0" + "eslint-plugin-react": "^7.32.0", + "semantic-release": "^24.0.0", + "semantic-release-monorepo": "^8.0.2" } } diff --git a/packages/gatsby-jaen-lens/gatsby/gatsby-node.ts b/packages/gatsby-jaen-lens/gatsby/gatsby-node.ts index 97a057b9..84ca20c4 100644 --- a/packages/gatsby-jaen-lens/gatsby/gatsby-node.ts +++ b/packages/gatsby-jaen-lens/gatsby/gatsby-node.ts @@ -1,4 +1,4 @@ -import {PageConfig} from '@atsnek/jaen' +import {PageConfig} from 'jaen' import {GatsbyNode, PluginOptions} from 'gatsby' export interface JaenLensPluginOptions extends PluginOptions { diff --git a/packages/gatsby-jaen-lens/package.json b/packages/gatsby-jaen-lens/package.json index 69f59bfc..867bf51a 100644 --- a/packages/gatsby-jaen-lens/package.json +++ b/packages/gatsby-jaen-lens/package.json @@ -1,6 +1,6 @@ { "name": "gatsby-jaen-lens", - "version": "0.0.17", + "version": "1.0.0", "description": "Snek lens plugin for Jaen to render proxied views of internal services behind a firewall.", "main": "index.js", "scripts": { @@ -14,7 +14,7 @@ ], "keywords": [ "gatsby", - "@atsnek/jaen", + "jaen", "snek-lens", "proxy" ], @@ -27,7 +27,6 @@ }, "peerDependencies": { "@chakra-ui/react": "^2.8.0", - "@snek-functions/origin": "^0.9.0", "react": "^18.2.0" }, "devDependencies": { diff --git a/packages/gatsby-jaen-lens/src/clients/lens/src/index.ts b/packages/gatsby-jaen-lens/src/clients/lens/src/index.ts index bd71f740..5110558d 100644 --- a/packages/gatsby-jaen-lens/src/clients/lens/src/index.ts +++ b/packages/gatsby-jaen-lens/src/clients/lens/src/index.ts @@ -1,4 +1,7 @@ import {makeSnekQuery} from 'snek-query' +import {User} from 'oidc-client-ts' +import 'jaen/dist/types' + import {Query, Mutation} from './schema.generated' const apiURL = process.env.GATSBY_LENS_API_URL @@ -10,6 +13,19 @@ if (!apiURL) { export const sq = makeSnekQuery( {Query, Mutation}, { - apiURL + apiURL, + middlewares: [ + ({context}) => { + const oidcStorage = sessionStorage.getItem( + `oidc.user:${__JAEN_ZITADEL__.authority}:${__JAEN_ZITADEL__.clientId}` + ) + + if (oidcStorage) { + const user = User.fromStorageString(oidcStorage) + + context.headers['Authorization'] = `Bearer ${user.access_token}` + } + } + ] } ) diff --git a/packages/gatsby-jaen-lens/src/components/PasswordUpdateForm/PasswordUpdateForm.tsx b/packages/gatsby-jaen-lens/src/components/PasswordUpdateForm/PasswordUpdateForm.tsx index 7440bddb..6ce47357 100644 --- a/packages/gatsby-jaen-lens/src/components/PasswordUpdateForm/PasswordUpdateForm.tsx +++ b/packages/gatsby-jaen-lens/src/components/PasswordUpdateForm/PasswordUpdateForm.tsx @@ -1,174 +1,163 @@ -import React, {useState} from 'react' import { - Box, Button, - FormControl, - FormErrorMessage, - FormHelperText, // Added FormHelperText + ButtonGroup, + FormControl, // Added FormHelperText FormLabel, + HStack, Input, - VStack, - Progress, - Text, - Stack, - Checkbox + List, + ListIcon, + ListItem, + Stack } from '@chakra-ui/react' -import {useForm, Controller} from 'react-hook-form' -import zxcvbn from 'zxcvbn' - -type PasswordResetFormValues = { - newPassword: string - confirmPassword: string - changeResourcePassword: boolean -} +import {FaCheck} from '@react-icons/all-files/fa/FaCheck' +import {FaX} from '@react-icons/all-files/fa6/FaX' +import React, {useState} from 'react' export interface PasswordUpdateFormProps { - onSubmit: (data: PasswordResetFormValues) => Promise - resource: {name: string} + passwordPolicy: { + minLength: number + hasSymbol: boolean + hasNumber: boolean + hasUppercase: boolean + hasLowercase: boolean + } + onPasswordUpdate: (currentPassword: string, password: string) => Promise } export const PasswordUpdateForm: React.FC = props => { - const [passwordStrength, setPasswordStrength] = useState(null) - const [passwordSuggestions, setPasswordSuggestions] = useState([]) + const [currentPassword, setCurrentPassword] = useState('') + const [password, setPassword] = useState('') + const [passwordConfirmation, setPasswordConfirmation] = useState('') - const { - register, - handleSubmit, - getValues, - control, - formState: {errors, isSubmitting} - } = useForm({ - defaultValues: { - changeResourcePassword: true - } - }) + const [isPasswordChanging, setIsPasswordChanging] = useState(false) - const onSubmit = async (data: PasswordResetFormValues) => { - await props.onSubmit(data) + const handlePasswordChange = async () => { + setIsPasswordChanging(true) + await props.onPasswordUpdate(currentPassword, password) + setIsPasswordChanging(false) } - const checkPasswordStrength = (password: string) => { - const result = zxcvbn(password) - setPasswordStrength(result.score) - setPasswordSuggestions(result.feedback.suggestions) - } + return ( + + + Enter the new password according to the policy below. + + + Current Password + setCurrentPassword(e.target.value)} + /> + - const getPasswordStrengthColor = () => { - if (passwordStrength === null) { - return 'gray' - } - // Define color codes for different password strengths - const colors = ['red', 'orange', 'yellow', 'green', 'teal'] - // Map the password strength score (0-4) to the colors - return colors[passwordStrength] - } + + {props.passwordPolicy.minLength && ( + + {password.length >= props.passwordPolicy.minLength ? ( + + ) : ( + + )} + Has to be at least {props.passwordPolicy.minLength} characters long. + ({password.length} / {props.passwordPolicy.minLength}) + + )} + {props.passwordPolicy.hasSymbol && ( + + {/[\p{P}\p{S}]/u.test(password) ? ( + + ) : ( + + )} + Must include a symbol or punctuation mark. + + )} - return ( - -
- - - - New password - { - // Custom validation for password strength - const strengthResult = zxcvbn(value) - if (strengthResult.score < 2) { - return 'Password strength is insufficient' - } - return true - } - }} - render={({field}) => ( - - { - field.onChange(e) - checkPasswordStrength(e.target.value) - }} - /> - - - )} - /> - {/* - Password Strength:{' '} - { - ['Very Weak', 'Weak', 'Fair', 'Strong', 'Very Strong'][ - passwordStrength || 0 - ] - } - */} - {passwordSuggestions.length > 0 && ( - - Suggestions: {passwordSuggestions.join(' ')} - - )} - - {errors.newPassword && errors.newPassword.message} - - + {props.passwordPolicy.hasNumber && ( + + {/\d/.test(password) ? ( + + ) : ( + + )} + Must include a number. + + )} + + {props.passwordPolicy.hasUppercase && ( + + {/[A-Z]/.test(password) ? ( + + ) : ( + + )} + Must include an uppercase letter. + + )} + + {props.passwordPolicy.hasLowercase && ( + + {/[a-z]/.test(password) ? ( + + ) : ( + + )} + Must include a lowercase letter. + + )} - - Confirm password - - value === getValues('newPassword') || - 'Passwords do not match' - }} - render={({field}) => } - /> - - {errors.confirmPassword && errors.confirmPassword.message} - - - + + {password && password === passwordConfirmation ? ( + + ) : ( + + )} + Passwords match. + + - - - - Change {props.resource.name} password - - - - When checked, the password reset will change all internal - passwords and the main password. - - + + + New Password + setPassword(e.target.value)} + /> + + + Confirm Password + setPasswordConfirmation(e.target.value)} + /> + + - - -
-
+ + + + +
) } diff --git a/packages/gatsby-jaen-lens/src/pages/lens/index.tsx b/packages/gatsby-jaen-lens/src/pages/lens/index.tsx index 965ffcf5..71b8a130 100644 --- a/packages/gatsby-jaen-lens/src/pages/lens/index.tsx +++ b/packages/gatsby-jaen-lens/src/pages/lens/index.tsx @@ -1,8 +1,9 @@ import { PageConfig, - useAuthenticationContext, + checkUserRoles, + useAuth, useNotificationsContext -} from '@atsnek/jaen' +} from 'jaen' import { Button, Heading, @@ -23,7 +24,6 @@ import { Thead, Tr } from '@chakra-ui/react' -import {getTokenPair, sq as origin} from '@snek-functions/origin' import {graphql, Link as GatsbyLink} from 'gatsby' import {useEffect, useState} from 'react' import {FaEdit} from 'react-icons/fa' @@ -37,13 +37,13 @@ import { import {IconChooser} from '../../components/IconChooser' const Page: React.FC = () => { - const {isAuthenticated, user} = useAuthenticationContext() + const {isAuthenticated, user} = useAuth() const {toast} = useNotificationsContext() const [isLoading, setIsLoading] = useState(true) const [services, setServices] = useState([]) - const isAdmin = user?.isAdmin + const isAdmin = checkUserRoles(user, ['268418173034829430:lens:admin']) useEffect(() => { const fetchServices = async () => { @@ -87,45 +87,23 @@ const Page: React.FC = () => { meta: LensServiceMetaInput } ): Promise => { - // refresh by calling userMe on origin - const [_, errors] = await origin.query(q => q.userMe.id) - - if (errors) { - toast({ - title: 'Error', - description: - 'Failed to refresh token. This is likely a bug or a network issue. Please try again later.', - status: 'error' - }) - return - } - - const {accessToken} = getTokenPair() - try { - const [data, errors] = await sq.mutate( - m => { - const service = m.serviceUpdate({ - id, - meta: inputData.meta - }) + const [data, errors] = await sq.mutate(m => { + const service = m.serviceUpdate({ + id, + meta: inputData.meta + }) - return { - id: service?.id, - fqdn: service?.fqdn, - host: service?.host, - port: service?.port, - meta: service?.meta, - isSecure: service?.isSecure, - __typename: service?.__typename - } - }, - { - headers: { - Authorization: `Bearer ${accessToken}` - } + return { + id: service?.id, + fqdn: service?.fqdn, + host: service?.host, + port: service?.port, + meta: service?.meta, + isSecure: service?.isSecure, + __typename: service?.__typename } - ) + }) if (errors) { throw new Error(errors[0]?.message) @@ -321,4 +299,4 @@ export const query = graphql` } ` -export {Head} from '@atsnek/jaen' +export {Head} from 'jaen' diff --git a/packages/gatsby-jaen-lens/src/pages/lens/password.tsx b/packages/gatsby-jaen-lens/src/pages/lens/password.tsx new file mode 100644 index 00000000..06500967 --- /dev/null +++ b/packages/gatsby-jaen-lens/src/pages/lens/password.tsx @@ -0,0 +1,120 @@ +import {Heading, Progress, Stack, StackDivider, Text} from '@chakra-ui/react' +import {navigate} from 'gatsby' +import { + AuthUserProvider, + PageConfig, + PageProps, + useAuthUser, + useNotificationsContext +} from 'jaen' + +import {sq} from '../../clients/lens/src' +import {PasswordUpdateForm} from '../../components/PasswordUpdateForm' + +const Page: React.FC = () => { + const {user, passwordPolicy, passwordUpdate} = useAuthUser() + const {toast} = useNotificationsContext() + + const handlePasswordChange = async ( + oldPassword: string, + newPassword: string + ) => { + const [_, errors] = await sq.mutate(m => + m.updateInternalPassword({password: newPassword}) + ) + + const success = !errors || errors.length === 0 + + if (success) { + toast({ + title: 'Success', + description: 'Password updated successfully', + status: 'success' + }) + + navigate('/lens/') + + await passwordUpdate(oldPassword, newPassword) + } else { + toast({ + title: 'Error', + description: + 'Failed to update password. This is likely a bug or a network issue. Please try again later.', + status: 'error' + }) + } + } + + if (!user) { + return + } + + return ( + } + id="coco" + spacing="4"> + + + Set internal password + + + + This sets the passwords for the internal services accessed through + Lens. After updating the password, you can access the internal + services using your username{' '} + <{user?.preferredLoginName}> and the password + you have set. + + + + + + + Note: This password is only valid for services connected to Lens + authentication. If you have any questions or issues, please reach out to + your administrator. + + + ) +} + +export const pageConfig: PageConfig = { + label: 'Lens Password', + icon: 'FaKey', + layout: { + name: 'jaen', + type: 'form' + }, + menu: { + type: 'user', + order: 500 + }, + auth: { + isRequired: true + }, + breadcrumbs: [ + { + label: 'Lens', + path: '/lens/' + }, + { + label: 'Password', + path: '/lens/password/' + } + ], + withoutJaenFrameStickyHeader: true +} + +export default props => { + return ( + + + + ) +} + +export {Head} from 'jaen' diff --git a/packages/gatsby-jaen-lens/src/pages/lens/service.tsx b/packages/gatsby-jaen-lens/src/pages/lens/service.tsx index cf817a5f..de0c88dc 100644 --- a/packages/gatsby-jaen-lens/src/pages/lens/service.tsx +++ b/packages/gatsby-jaen-lens/src/pages/lens/service.tsx @@ -1,5 +1,5 @@ import {Box} from '@chakra-ui/react' -import {PageConfig, PageProps} from '@atsnek/jaen' +import {PageConfig, PageProps} from 'jaen' import {useEffect, useState} from 'react' const Page: React.FC = ({data, location}) => { @@ -52,4 +52,4 @@ export const pageConfig: PageConfig = { export default Page -export {Head} from '@atsnek/jaen' +export {Head} from 'jaen' diff --git a/packages/gatsby-jaen-mailpress/package.json b/packages/gatsby-jaen-mailpress/package.json index 5494be5d..8c6037b2 100644 --- a/packages/gatsby-jaen-mailpress/package.json +++ b/packages/gatsby-jaen-mailpress/package.json @@ -13,7 +13,7 @@ "gatsby-node.js" ], "scripts": { - "build": "tsc gatsby/* --outDir dist/gatsby --esModuleInterop --skipLibCheck --resolveJsonModule" + "build": "tsc gatsby/* --outDir dist/gatsby --esModuleInterop --skipLibCheck --resolveJsonModule && tsc" }, "dependencies": { "@monaco-editor/react": "^4.6.0", @@ -29,7 +29,7 @@ "@snek-functions/origin": "^0.9.0", "gatsby": "^5.12.4", "react": "^18.2.0", - "@atsnek/jaen": "^1.0.0-rc.55" + "jaen": "^1.0.0" }, "devDependencies": {} } diff --git a/packages/gatsby-jaen-mailpress/src/client/src/index.ts b/packages/gatsby-jaen-mailpress/src/client/src/index.ts index 28bab904..9f477f32 100644 --- a/packages/gatsby-jaen-mailpress/src/client/src/index.ts +++ b/packages/gatsby-jaen-mailpress/src/client/src/index.ts @@ -1,6 +1,6 @@ import {makeSnekQuery} from 'snek-query' import {User} from 'oidc-client-ts' -import '@atsnek/jaen/dist/types' +import 'jaen/dist/types' import {Query, Mutation} from './schema.generated' diff --git a/packages/gatsby-jaen-mailpress/src/client/src/schema.generated.ts b/packages/gatsby-jaen-mailpress/src/client/src/schema.generated.ts index ce371cab..3665d86a 100644 --- a/packages/gatsby-jaen-mailpress/src/client/src/schema.generated.ts +++ b/packages/gatsby-jaen-mailpress/src/client/src/schema.generated.ts @@ -1,4 +1,4 @@ - +// @ts-nocheck import { proxy, arrayProxy, fnProxy, fnArrayProxy, t } from "snek-query"; export enum OAuthProvider { diff --git a/packages/gatsby-jaen-mailpress/src/hooks.ts b/packages/gatsby-jaen-mailpress/src/hooks.ts deleted file mode 100644 index 61e4c1db..00000000 --- a/packages/gatsby-jaen-mailpress/src/hooks.ts +++ /dev/null @@ -1,239 +0,0 @@ -import {snekResourceId, useNotificationsContext} from '@atsnek/jaen' -import {sq} from '@snek-functions/origin' - -import React, {useCallback, useEffect, useState} from 'react' - -import {Mutation} from '@snek-functions/origin/dist/schema.generated' - -type UserCreate = Parameters[0] -type UserUpdate = Parameters[0] - -export const useUser = (userId: string) => { - const {toast} = useNotificationsContext() - const [isLoading, setIsLoading] = useState(true) - const [user, setUser] = React.useState<{ - id: string - primaryEmailAddress: string - username: string - createdAt: string - details?: { - avatarURL?: string - firstName?: string - lastName?: string - } - isActive: boolean - isAdmin: boolean - roles: Array<{id: string; description: string}> - }>() - - const checkErrors = (errors: Array<{message: string}>) => { - if (errors?.length > 0) { - toast({ - title: 'Error', - description: errors[0]?.message, - status: 'error', - duration: 5000, - isClosable: true - }) - } - - return !errors || errors.length === 0 - } - - const fetchUser = useCallback(async () => { - const [user, errors] = await sq.query(Query => { - const user = Query.user({id: userId, resourceId: snekResourceId}) - - console.log('USER', user) - - return { - id: user.id, - primaryEmailAddress: user.primaryEmailAddress, - username: user.username, - createdAt: user.createdAt, - details: { - avatarURL: user.details?.avatarURL || undefined, - firstName: user.details?.firstName || undefined, - lastName: user.details?.lastName || undefined - }, - isActive: user.isActive, - isAdmin: user.isAdmin, - roles: user.roles.map(role => ({ - id: role.id, - description: role.description - })) - } - }) - - const ok = checkErrors(errors) - - setUser(user) - setIsLoading(false) - }, []) - - useEffect(() => { - fetchUser() - }, []) - - return { - user, - isLoading - } -} - -export const useUsers = () => { - const {toast} = useNotificationsContext() - const [isLoading, setIsLoading] = useState(true) - - const [users, setUsers] = React.useState< - { - id: string - primaryEmailAddress: string - username: string - createdAt: string - details?: { - avatarURL?: string - firstName?: string - lastName?: string - } - isActive: boolean - isAdmin: boolean - }[] - >([]) - - const checkErrors = (errors: Array<{message: string}>) => { - if (errors?.length > 0) { - toast({ - title: 'Error', - description: errors[0]?.message, - status: 'error', - duration: 5000, - isClosable: true - }) - } - - return !errors || errors.length === 0 - } - - const fetchUsers = useCallback(async () => { - const [users, errors] = await sq.query(Query => - Query.allUser({resourceId: snekResourceId}).map(user => ({ - id: user?.id, - primaryEmailAddress: user?.primaryEmailAddress, - username: user?.username, - createdAt: user?.createdAt, - details: { - avatarURL: user?.details?.avatarURL || undefined, - firstName: user?.details?.firstName || undefined, - lastName: user?.details?.lastName || undefined - }, - isActive: user?.isActive, - isAdmin: user?.isAdmin - })) - ) - - const ok = checkErrors(errors) - - setUsers(users as any) - setIsLoading(false) - }, []) - - useEffect(() => { - fetchUsers() - }, []) - - const addUser = async (values: UserCreate['values']) => { - const [newUser, errors] = await sq.mutate(Mutation => { - const user = Mutation.userCreate({ - resourceId: snekResourceId, - values, - skipEmailVerification: true - }) - - return { - id: user?.id, - primaryEmailAddress: user?.primaryEmailAddress, - username: user?.username, - createdAt: user?.createdAt, - details: { - firstName: user?.details?.firstName || undefined, - lastName: user?.details?.lastName || undefined - }, - isActive: user?.isActive, - isAdmin: user?.isAdmin - } - }) - - const ok = checkErrors(errors) - - if (ok) { - setUsers([...users, newUser] as any) - - toast({ - title: 'Success', - description: 'User created', - status: 'success', - duration: 5000, - isClosable: true - }) - } - - return ok - } - - const updateUser = async ( - id: UserUpdate['id'], - values: UserUpdate['values'] - ) => { - const [updatedUser, errors] = await sq.mutate(Mutation => { - const user = Mutation.userUpdate({ - id, - values - }) - - return { - id: user?.id, - primaryEmailAddress: user?.primaryEmailAddress, - username: user?.username, - createdAt: user?.createdAt, - details: { - firstName: user?.details?.firstName || undefined, - lastName: user?.details?.lastName || undefined - }, - isActive: user?.isActive, - isAdmin: user?.isAdmin - } - }) - - const ok = checkErrors(errors) - - if (ok) { - setUsers(users.map(u => (u.id === id ? updatedUser : u)) as any) - } - - return ok - } - - const deleteUser = async (userId: string) => { - const [deletedUser, errors] = await sq.mutate(Mutation => - Mutation.userDelete({id: userId}) - ) - - const ok = checkErrors(errors) - - if (ok) { - setUsers(users.filter(u => u.id !== userId)) - } - - return ok - } - - return { - users, - addUser, - updateUser, - deleteUser, - - isLoading - } -} diff --git a/packages/gatsby-jaen-mailpress/src/index.ts b/packages/gatsby-jaen-mailpress/src/index.ts new file mode 100644 index 00000000..e2668173 --- /dev/null +++ b/packages/gatsby-jaen-mailpress/src/index.ts @@ -0,0 +1 @@ +export {sq} from './client/src/index' diff --git a/packages/gatsby-jaen-mailpress/src/pages/mailpress/index.tsx b/packages/gatsby-jaen-mailpress/src/pages/mailpress/index.tsx index d6679265..b85878c0 100644 --- a/packages/gatsby-jaen-mailpress/src/pages/mailpress/index.tsx +++ b/packages/gatsby-jaen-mailpress/src/pages/mailpress/index.tsx @@ -1,4 +1,4 @@ -import {PageConfig, PageProps} from '@atsnek/jaen' +import {PageConfig, PageProps} from 'jaen' import {useEffect} from 'react' import {navigate} from 'gatsby' diff --git a/packages/gatsby-jaen-mailpress/src/pages/mailpress/templates/[templateId].tsx b/packages/gatsby-jaen-mailpress/src/pages/mailpress/templates/[templateId].tsx index 59ffbea0..6d136dcb 100644 --- a/packages/gatsby-jaen-mailpress/src/pages/mailpress/templates/[templateId].tsx +++ b/packages/gatsby-jaen-mailpress/src/pages/mailpress/templates/[templateId].tsx @@ -1,13 +1,8 @@ -import {PageConfig, PageProps, useNotificationsContext} from '@atsnek/jaen' +import {PageConfig, PageProps, useNotificationsContext} from 'jaen' import {useEffect, useMemo} from 'react' import {CopyIcon, DeleteIcon} from '@chakra-ui/icons' import { - Accordion, - AccordionButton, - AccordionIcon, - AccordionItem, - AccordionPanel, Box, Button, ButtonGroup, @@ -19,7 +14,6 @@ import { FormControl, FormErrorMessage, FormLabel, - HStack, Heading, IconButton, Input, @@ -40,8 +34,7 @@ import { Th, Thead, Tr, - UnorderedList, - VStack + UnorderedList } from '@chakra-ui/react' import {Editor} from '@monaco-editor/react' import {Link as GatsbyLink, navigate} from 'gatsby' @@ -49,11 +42,6 @@ import {sanitize} from 'isomorphic-dompurify' import {Controller, useFieldArray, useForm} from 'react-hook-form' import {useQuery} from 'snek-query/react-hooks' import {sq} from '../../../client/src' -import { - EmailTemplate, - VariableDefinition -} from '../../../client/src/schema.generated' -import templates from '.' const Page: React.FC = ({params}) => { const templateId = params.templateId @@ -233,7 +221,7 @@ const Page: React.FC = ({params}) => { }) if (confirmed) { // delete using sq - const [data, errors] = await sq.mutate(m => + const [_, errors] = await sq.mutate(m => m.templateDelete({ id: templateId }) @@ -367,7 +355,7 @@ const Page: React.FC = ({params}) => { To - {envelopeToField.fields.map((field, index) => ( + {envelopeToField.fields.map((_, index) => ( = ({params}) => { height="var(--chakra-sizes-xs)" defaultLanguage="javascript" defaultValue={field.value || undefined} - onChange={(value, event) => field.onChange(value)} + onChange={(value, _) => field.onChange(value)} /> )} /> @@ -454,7 +442,7 @@ const Page: React.FC = ({params}) => { height="var(--chakra-sizes-md)" defaultLanguage="html" defaultValue={field.value || undefined} - onChange={(value, event) => field.onChange(value)} + onChange={(value, _) => field.onChange(value)} /> )} /> @@ -577,304 +565,6 @@ const Page: React.FC = ({params}) => { ) - - // Form with chakraui components - return ( - - Email Template - - {/* Template ID with copy button */} - - - Template ID - - } - variant="outline" - onClick={onCopy}> - - - -
- - - {/* Description Field */} - - - Description - - - {errors.description?.message} - - - - - {/* Parent Field */} - - - Parent - - - - - {/* Linked Field just for display and link to template */} - - - Linked - {template?.linked?.length ? ( - - {template?.linked?.map(t => ( - - - {t.description} ({t.id}) - - - ))} - - ) : ( - No linked templates - )} - - - - {/* Envelope Card */} - - - Envelope - - - - {/* Subject Field */} - - - Subject - - - - - {/* From Field */} - - - From - - - - - - - - - {/* To Field */} - - - To - - - - - {/* Reply To Field */} - - - Reply To - - - - - - - - - - - - {/* Transformer Card */} - - - Transformer - - - - {/* Transformer Field */} - - - ( - field.onChange(value)} - /> - )} - /> - - - - - - - {/* Content Card */} - - - Content - - - - - {/* Content Field */} - - - ( - field.onChange(value)} - /> - )} - /> - - - - - {/* Preview */} - - Preview - - - - - - - - - {/* Variables Card */} - - - Variables - - - - {/* Variables Field */} - - -