Compare commits
	
		
			1 Commits
		
	
	
		
			renovate/t
			...
			4922739106
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4922739106 | 
| @@ -14,7 +14,7 @@ steps: | ||||
|   - cargo test | ||||
|  | ||||
| - name: app_deploy | ||||
|   image: node:24 | ||||
|   image: node:22 | ||||
|   environment: | ||||
|     AWS_ACCESS_KEY_ID: | ||||
|       from_secret: AWS_ACCESS_KEY_ID | ||||
| @@ -24,7 +24,7 @@ steps: | ||||
|   commands: | ||||
|   # Build website | ||||
|   - cd geneit_app | ||||
|   - npm install | ||||
|   - npm install  | ||||
|   - GENERATE_SOURCEMAP=false npm run build  | ||||
|   # Install AWS | ||||
|   - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" | ||||
|   | ||||
| @@ -1,28 +0,0 @@ | ||||
| import js from '@eslint/js' | ||||
| import globals from 'globals' | ||||
| import reactHooks from 'eslint-plugin-react-hooks' | ||||
| import reactRefresh from 'eslint-plugin-react-refresh' | ||||
| import tseslint from 'typescript-eslint' | ||||
|  | ||||
| export default tseslint.config( | ||||
|   { ignores: ['dist'] }, | ||||
|   { | ||||
|     extends: [js.configs.recommended, ...tseslint.configs.recommended], | ||||
|     files: ['**/*.{ts,tsx}'], | ||||
|     languageOptions: { | ||||
|       ecmaVersion: 2020, | ||||
|       globals: globals.browser, | ||||
|     }, | ||||
|     plugins: { | ||||
|       'react-hooks': reactHooks, | ||||
|       'react-refresh': reactRefresh, | ||||
|     }, | ||||
|     rules: { | ||||
|       ...reactHooks.configs.recommended.rules, | ||||
|       'react-refresh/only-export-components': [ | ||||
|         'warn', | ||||
|         { allowConstantExport: true }, | ||||
|       ], | ||||
|     }, | ||||
|   }, | ||||
| ) | ||||
							
								
								
									
										3708
									
								
								geneit_app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3708
									
								
								geneit_app/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -3,54 +3,70 @@ | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "type": "module", | ||||
|   "dependencies": { | ||||
|     "@babel/plugin-proposal-private-property-in-object": "^7.21.11", | ||||
|     "@emotion/react": "^11.13.3", | ||||
|     "@emotion/styled": "^11.13.0", | ||||
|     "@fontsource/roboto": "^5.1.0", | ||||
|     "@fullcalendar/core": "^6.1.15", | ||||
|     "@fullcalendar/daygrid": "^6.1.15", | ||||
|     "@fullcalendar/interaction": "^6.1.15", | ||||
|     "@fullcalendar/list": "^6.1.15", | ||||
|     "@fullcalendar/react": "^6.1.15", | ||||
|     "@mdi/js": "^7.2.96", | ||||
|     "@mdi/react": "^1.6.1", | ||||
|     "@mui/icons-material": "^6.1.2", | ||||
|     "@mui/lab": "^6.0.0-beta.10", | ||||
|     "@mui/material": "^6.1.2", | ||||
|     "@mui/x-data-grid": "^7.18.0", | ||||
|     "@mui/x-date-pickers": "^7.18.0", | ||||
|     "@mui/x-tree-view": "^7.18.0", | ||||
|     "@testing-library/jest-dom": "^6.5.0", | ||||
|     "@testing-library/react": "^16.0.1", | ||||
|     "@testing-library/user-event": "^14.0.0", | ||||
|     "@types/jest": "^29.5.13", | ||||
|     "@types/react": "^18.3.11", | ||||
|     "@types/react-dom": "^18.3.0", | ||||
|     "@vitejs/plugin-react": "^4.3.2", | ||||
|     "date-and-time": "^3.6.0", | ||||
|     "dayjs": "^1.11.13", | ||||
|     "email-validator": "^2.0.4", | ||||
|     "filesize": "^10.1.6", | ||||
|     "jspdf": "^2.5.2", | ||||
|     "mui-color-input": "^5.0.0", | ||||
|     "react": "^18.3.1", | ||||
|     "react-dom": "^18.3.1", | ||||
|     "react-easy-crop": "^5.0.8", | ||||
|     "react-qr-code": "^2.0.14", | ||||
|     "react-router-dom": "^7.0.0", | ||||
|     "react-zoom-pan-pinch": "^3.4.4", | ||||
|     "svg2pdf.js": "^2.2.3", | ||||
|     "typescript": "^5.6.2", | ||||
|     "vite": "^6.0.0", | ||||
|     "vite-tsconfig-paths": "^5.0.1", | ||||
|     "web-vitals": "^3.5.2" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "dev": "vite", | ||||
|     "build": "tsc -b && vite build", | ||||
|     "lint": "eslint .", | ||||
|     "start": "vite", | ||||
|     "build": "tsc && vite build", | ||||
|     "preview": "vite preview" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@emotion/react": "^11.14.0", | ||||
|     "@emotion/styled": "^11.14.1", | ||||
|     "@fontsource/roboto": "^5.2.8", | ||||
|     "@fullcalendar/core": "^6.1.19", | ||||
|     "@fullcalendar/daygrid": "^6.1.19", | ||||
|     "@fullcalendar/interaction": "^6.1.19", | ||||
|     "@fullcalendar/list": "^6.1.19", | ||||
|     "@fullcalendar/react": "^6.1.19", | ||||
|     "@mdi/js": "^7.4.47", | ||||
|     "@mdi/react": "^1.6.1", | ||||
|     "@mui/icons-material": "^7.2.0", | ||||
|     "@mui/lab": "^7.0.0-beta.17", | ||||
|     "@mui/material": "^7.2.0", | ||||
|     "@mui/x-data-grid": "^8.15.0", | ||||
|     "@mui/x-date-pickers": "^8.15.0", | ||||
|     "@mui/x-tree-view": "^8.15.0", | ||||
|     "date-and-time": "^3.6.0", | ||||
|     "dayjs": "^1.11.18", | ||||
|     "email-validator": "^2.0.4", | ||||
|     "filesize": "^11.0.13", | ||||
|     "jspdf": "^3.0.3", | ||||
|     "mui-color-input": "^7.0.0", | ||||
|     "react": "^19.2.0", | ||||
|     "react-dom": "^19.2.0", | ||||
|     "react-easy-crop": "^5.5.3", | ||||
|     "react-qr-code": "^2.0.18", | ||||
|     "react-router-dom": "^7.4.0", | ||||
|     "react-zoom-pan-pinch": "^3.7.0", | ||||
|     "svg2pdf.js": "^2.6.0" | ||||
|   "eslintConfig": { | ||||
|     "extends": [ | ||||
|       "react-app", | ||||
|       "react-app/jest" | ||||
|     ] | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@eslint/js": "^9.38.0", | ||||
|     "@types/react": "^19.2.2", | ||||
|     "@types/react-dom": "^19.2.2", | ||||
|     "@vitejs/plugin-react": "^4.7.0", | ||||
|     "eslint": "^9.38.0", | ||||
|     "eslint-plugin-react-hooks": "^5.2.0", | ||||
|     "eslint-plugin-react-refresh": "^0.4.24", | ||||
|     "globals": "^16.4.0", | ||||
|     "typescript": "^5.9.3", | ||||
|     "typescript-eslint": "^8.46.2", | ||||
|     "vite": "^7.0.6" | ||||
|   "browserslist": { | ||||
|     "production": [ | ||||
|       ">0.2%", | ||||
|       "not dead", | ||||
|       "not op_mini all" | ||||
|     ], | ||||
|     "development": [ | ||||
|       "last 1 chrome version", | ||||
|       "last 1 firefox version", | ||||
|       "last 1 safari version" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -59,10 +59,7 @@ export function App(): React.ReactElement { | ||||
|  | ||||
|   const context: AuthContext = { | ||||
|     signedIn: signedIn, | ||||
|     setSignedIn: (s) => { | ||||
|       location.reload(); | ||||
|       setSignedIn(s); | ||||
|     }, | ||||
|     setSignedIn: (s) => setSignedIn(s), | ||||
|   }; | ||||
|  | ||||
|   const router = createBrowserRouter( | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client"; | ||||
| import { App } from "./App"; | ||||
| import { ServerApi } from "./api/ServerApi"; | ||||
| import "./index.css"; | ||||
| import reportWebVitals from "./reportWebVitals"; | ||||
|  | ||||
| // Roboto font | ||||
| import "@fontsource/roboto/300.css"; | ||||
| @@ -60,3 +61,8 @@ async function init() { | ||||
| } | ||||
|  | ||||
| init(); | ||||
|  | ||||
| // If you want to start measuring performance in your app, pass a function | ||||
| // to log results (for example: reportWebVitals(console.log)) | ||||
| // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals | ||||
| reportWebVitals(); | ||||
|   | ||||
							
								
								
									
										15
									
								
								geneit_app/src/reportWebVitals.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								geneit_app/src/reportWebVitals.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| import { ReportHandler } from 'web-vitals'; | ||||
|  | ||||
| const reportWebVitals = (onPerfEntry?: ReportHandler) => { | ||||
|   if (onPerfEntry && onPerfEntry instanceof Function) { | ||||
|     import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { | ||||
|       getCLS(onPerfEntry); | ||||
|       getFID(onPerfEntry); | ||||
|       getFCP(onPerfEntry); | ||||
|       getLCP(onPerfEntry); | ||||
|       getTTFB(onPerfEntry); | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export default reportWebVitals; | ||||
| @@ -1,5 +1,4 @@ | ||||
| import VisibilityIcon from "@mui/icons-material/Visibility"; | ||||
| import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; | ||||
| import { Visibility, VisibilityOff } from "@mui/icons-material"; | ||||
| import { | ||||
|   Alert, | ||||
|   CircularProgress, | ||||
| @@ -12,7 +11,7 @@ import { | ||||
| } from "@mui/material"; | ||||
| import Box from "@mui/material/Box"; | ||||
| import Button from "@mui/material/Button"; | ||||
| import Grid from "@mui/material/Grid"; | ||||
| import Grid from "@mui/material/Grid2"; | ||||
| import TextField from "@mui/material/TextField"; | ||||
| import Typography from "@mui/material/Typography"; | ||||
| import * as React from "react"; | ||||
| @@ -149,7 +148,7 @@ export function LoginRoute(): React.ReactElement { | ||||
|                     onMouseDown={handleMouseDownPassword} | ||||
|                     edge="end" | ||||
|                   > | ||||
|                     {showPassword ? <VisibilityOffIcon /> : <VisibilityIcon />} | ||||
|                     {showPassword ? <VisibilityOff /> : <Visibility />} | ||||
|                   </IconButton> | ||||
|                 </Tooltip> | ||||
|               </InputAdornment> | ||||
|   | ||||
| @@ -137,10 +137,8 @@ function UsersTable(p: { | ||||
|             <GridActionsCellItem | ||||
|               icon={<SaveIcon />} | ||||
|               label="Save" | ||||
|               material={{ | ||||
|                 sx: { | ||||
|                   color: 'primary.main', | ||||
|                 }, | ||||
|               sx={{ | ||||
|                 color: "primary.main", | ||||
|               }} | ||||
|               onClick={handleSaveClick(id)} | ||||
|             />, | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import EditIcon from "@mui/icons-material/Edit"; | ||||
| import FileDownloadIcon from "@mui/icons-material/FileDownload"; | ||||
| import SaveIcon from "@mui/icons-material/Save"; | ||||
| import { Button, Stack } from "@mui/material"; | ||||
| import Grid from "@mui/material/Grid"; | ||||
| import Grid from "@mui/material/Grid2"; | ||||
| import React from "react"; | ||||
| import { useNavigate, useParams } from "react-router-dom"; | ||||
| import { ServerApi } from "../../../api/ServerApi"; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import { useFamily } from "../../../widgets/BaseFamilyRoute"; | ||||
| import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle"; | ||||
|  | ||||
| export function FamilyHomeRoute(): React.ReactElement { | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import { | ||||
|   ListItemText, | ||||
|   Stack, | ||||
| } from "@mui/material"; | ||||
| import Grid from "@mui/material/Grid"; | ||||
| import Grid from "@mui/material/Grid2"; | ||||
| import * as EmailValidator from "email-validator"; | ||||
| import React from "react"; | ||||
| import { useNavigate, useParams } from "react-router-dom"; | ||||
|   | ||||
							
								
								
									
										5
									
								
								geneit_app/src/setupTests.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								geneit_app/src/setupTests.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| // jest-dom adds custom jest matchers for asserting on DOM nodes. | ||||
| // allows you to do things like: | ||||
| // expect(element).toHaveTextContent(/react/i) | ||||
| // learn more: https://github.com/testing-library/jest-dom | ||||
| import '@testing-library/jest-dom'; | ||||
| @@ -43,13 +43,9 @@ export function BaseAuthenticatedPage(): React.ReactElement { | ||||
|     setAnchorEl(null); | ||||
|   }; | ||||
|  | ||||
|   const signOut = async () => { | ||||
|   const signOut = () => { | ||||
|     handleCloseMenu(); | ||||
|     try { | ||||
|       await AuthApi.SignOut(); | ||||
|     } catch (e) { | ||||
|       console.error(e); | ||||
|     } | ||||
|     AuthApi.SignOut(); | ||||
|     navigate("/"); | ||||
|     auth.setSignedIn(false); | ||||
|   }; | ||||
|   | ||||
| @@ -54,7 +54,7 @@ export function BaseFamilyRoute(): React.ReactElement { | ||||
|  | ||||
|   const loadKey = React.useRef(1); | ||||
|  | ||||
|   const loadPromise = React.useRef<() => void>(null); | ||||
|   const loadPromise = React.useRef<() => void>(); | ||||
|  | ||||
|   const load = async () => { | ||||
|     const familyID = Number(familyId); | ||||
| @@ -104,7 +104,7 @@ export function BaseFamilyRoute(): React.ReactElement { | ||||
|       build={() => { | ||||
|         if (loadPromise.current != null) { | ||||
|           loadPromise.current?.(); | ||||
|           loadPromise.current = null; | ||||
|           loadPromise.current = undefined; | ||||
|         } | ||||
|  | ||||
|         return ( | ||||
|   | ||||
| @@ -3,9 +3,10 @@ import Icon from "@mdi/react"; | ||||
| import Avatar from "@mui/material/Avatar"; | ||||
| import Box from "@mui/material/Box"; | ||||
| import CssBaseline from "@mui/material/CssBaseline"; | ||||
| import Grid from "@mui/material/Grid"; | ||||
| import Grid from "@mui/material/Grid2"; | ||||
| import Paper from "@mui/material/Paper"; | ||||
| import Typography from "@mui/material/Typography"; | ||||
| import * as React from "react"; | ||||
| import { Link, Outlet } from "react-router-dom"; | ||||
| import { DarkThemeButton } from "./DarkThemeButton"; | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import VisibilityIcon from "@mui/icons-material/Visibility"; | ||||
| import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; | ||||
| import { Visibility, VisibilityOff } from "@mui/icons-material"; | ||||
| import { | ||||
|   FormControl, | ||||
|   FormHelperText, | ||||
| @@ -51,7 +50,7 @@ export function PasswordInput(p: { | ||||
|               onMouseDown={handleMouseDownPassword} | ||||
|               edge="end" | ||||
|             > | ||||
|               {showPassword ? <VisibilityOffIcon /> : <VisibilityIcon />} | ||||
|               {showPassword ? <VisibilityOff /> : <Visibility />} | ||||
|             </IconButton> | ||||
|           </InputAdornment> | ||||
|         } | ||||
|   | ||||
| @@ -27,7 +27,7 @@ export function BaseAccommodationsRoute(): React.ReactElement { | ||||
|  | ||||
|   const loadKey = React.useRef(1); | ||||
|  | ||||
|   const loadPromise = React.useRef<() => void>(null); | ||||
|   const loadPromise = React.useRef<() => void>(); | ||||
|  | ||||
|   const load = async () => { | ||||
|     setAccommodations( | ||||
| @@ -53,7 +53,7 @@ export function BaseAccommodationsRoute(): React.ReactElement { | ||||
|       build={() => { | ||||
|         if (loadPromise.current != null) { | ||||
|           loadPromise.current?.(); | ||||
|           loadPromise.current = null; | ||||
|           loadPromise.current = undefined; | ||||
|         } | ||||
|  | ||||
|         return ( | ||||
|   | ||||
| @@ -22,7 +22,7 @@ export function BaseGenealogyRoute(): React.ReactElement { | ||||
|  | ||||
|   const loadKey = React.useRef(1); | ||||
|  | ||||
|   const loadPromise = React.useRef<() => void>(null); | ||||
|   const loadPromise = React.useRef<() => void>(); | ||||
|  | ||||
|   const load = async () => { | ||||
|     setMembers(await MemberApi.GetEntireList(family.familyId)); | ||||
| @@ -48,7 +48,7 @@ export function BaseGenealogyRoute(): React.ReactElement { | ||||
|       build={() => { | ||||
|         if (loadPromise.current != null) { | ||||
|           loadPromise.current?.(); | ||||
|           loadPromise.current = null; | ||||
|           loadPromise.current = undefined; | ||||
|         } | ||||
|  | ||||
|         return ( | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", | ||||
|     "target": "ES2020", | ||||
|     "useDefineForClassFields": true, | ||||
|     "lib": ["ES2020", "DOM", "DOM.Iterable"], | ||||
|     "module": "ESNext", | ||||
|     "skipLibCheck": true, | ||||
|  | ||||
|     /* Bundler mode */ | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowImportingTsExtensions": true, | ||||
|     "isolatedModules": true, | ||||
|     "moduleDetection": "force", | ||||
|     "noEmit": true, | ||||
|     "jsx": "react-jsx", | ||||
|  | ||||
|     /* Linting */ | ||||
|     "strict": true, | ||||
|     "noUnusedLocals": true, | ||||
|     "noUnusedParameters": true, | ||||
|     "noFallthroughCasesInSwitch": true, | ||||
|     "noUncheckedSideEffectImports": true | ||||
|   }, | ||||
|   "include": ["src"] | ||||
| } | ||||
| @@ -1,7 +1,21 @@ | ||||
| { | ||||
|   "files": [], | ||||
|   "references": [ | ||||
|     { "path": "./tsconfig.app.json" }, | ||||
|     { "path": "./tsconfig.node.json" } | ||||
|   ] | ||||
|   "compilerOptions": { | ||||
|     "target": "ESNext", | ||||
|     "lib": ["dom", "dom.iterable", "esnext"], | ||||
|     "types": ["vite/client"], | ||||
|     "allowJs": true, | ||||
|     "skipLibCheck": true, | ||||
|     "esModuleInterop": true, | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "strict": true, | ||||
|     "forceConsistentCasingInFileNames": true, | ||||
|     "noFallthroughCasesInSwitch": true, | ||||
|     "module": "esnext", | ||||
|     "moduleResolution": "node", | ||||
|     "resolveJsonModule": true, | ||||
|     "isolatedModules": true, | ||||
|     "noEmit": true, | ||||
|     "jsx": "react-jsx" | ||||
|   }, | ||||
|   "include": ["src"] | ||||
| } | ||||
|   | ||||
| @@ -1,24 +0,0 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", | ||||
|     "target": "ES2022", | ||||
|     "lib": ["ES2023"], | ||||
|     "module": "ESNext", | ||||
|     "skipLibCheck": true, | ||||
|  | ||||
|     /* Bundler mode */ | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowImportingTsExtensions": true, | ||||
|     "isolatedModules": true, | ||||
|     "moduleDetection": "force", | ||||
|     "noEmit": true, | ||||
|  | ||||
|     /* Linting */ | ||||
|     "strict": true, | ||||
|     "noUnusedLocals": true, | ||||
|     "noUnusedParameters": true, | ||||
|     "noFallthroughCasesInSwitch": true, | ||||
|     "noUncheckedSideEffectImports": true | ||||
|   }, | ||||
|   "include": ["vite.config.ts"] | ||||
| } | ||||
| @@ -1,7 +1,15 @@ | ||||
| import { defineConfig } from 'vite' | ||||
| import react from '@vitejs/plugin-react' | ||||
| import { defineConfig } from "vite"; | ||||
| import react from "@vitejs/plugin-react"; | ||||
| import viteTsconfigPaths from "vite-tsconfig-paths"; | ||||
|  | ||||
| // https://vite.dev/config/ | ||||
| export default defineConfig({ | ||||
|   plugins: [react()], | ||||
| }) | ||||
|   // depending on your application, base can also be "/" | ||||
|   base: "/", | ||||
|   plugins: [react(), viteTsconfigPaths()], | ||||
|   server: { | ||||
|     // this ensures that the browser opens upon server start | ||||
|     open: true, | ||||
|     // this sets a default port to 3000 | ||||
|     port: 3000, | ||||
|   }, | ||||
| }); | ||||
|   | ||||
							
								
								
									
										1638
									
								
								geneit_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1638
									
								
								geneit_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,43 +1,43 @@ | ||||
| [package] | ||||
| name = "geneit_backend" | ||||
| version = "0.1.0" | ||||
| edition = "2024" | ||||
| edition = "2021" | ||||
|  | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| [dependencies] | ||||
| log = "0.4.28" | ||||
| env_logger = "0.11.8" | ||||
| clap = { version = "4.5.50", features = ["derive", "env"] } | ||||
| log = "0.4.21" | ||||
| env_logger = "0.11.5" | ||||
| clap = { version = "4.5.17", features = ["derive", "env"] } | ||||
| lazy_static = "1.5.0" | ||||
| lazy-regex = "3.4.1" | ||||
| anyhow = "1.0.100" | ||||
| actix-web = "4.11.0" | ||||
| actix-cors = "0.7.1" | ||||
| actix-multipart = "0.7.2" | ||||
| lazy-regex = "3.3.0" | ||||
| anyhow = "1.0.87" | ||||
| actix-web = "4.9.0" | ||||
| actix-cors = "0.7.0" | ||||
| actix-multipart = "0.7.0" | ||||
| actix-remote-ip = "0.1.0" | ||||
| futures-util = "0.3.31" | ||||
| diesel = { version = "2.2.12", features = ["postgres"] } | ||||
| diesel_migrations = "2.2.0" | ||||
| serde = { version = "1.0.228", features = ["derive"] } | ||||
| serde_json = "1.0.145" | ||||
| mailchecker = "6.0.19" | ||||
| redis = "0.32.7" | ||||
| lettre = "0.11.19" | ||||
| rand = "0.9.2" | ||||
| bcrypt = "0.17.1" | ||||
| light-openid = "1.0.4" | ||||
| thiserror = "2.0.17" | ||||
| serde_with = "3.14.0" | ||||
| rust_iso3166 = "0.1.14" | ||||
| futures-util = "0.3.30" | ||||
| diesel = { version = "2.2.4", features = ["postgres"] } | ||||
| diesel_migrations = "2.1.0" | ||||
| serde = { version = "1.0.210", features = ["derive"] } | ||||
| serde_json = "1.0.128" | ||||
| mailchecker = "6.0.8" | ||||
| redis = "0.27.0" | ||||
| lettre = "0.11.8" | ||||
| rand = "0.8.5" | ||||
| bcrypt = "0.16.0" | ||||
| light-openid = "1.0.2" | ||||
| thiserror = "1.0.60" | ||||
| serde_with = "3.8.1" | ||||
| rust_iso3166 = "0.1.12" | ||||
| rust-s3 = "0.35.1" | ||||
| sha2 = "0.10.9" | ||||
| image = "0.25.8" | ||||
| uuid = { version = "1.17.0", features = ["v4"] } | ||||
| sha2 = "0.10.8" | ||||
| image = "0.25.1" | ||||
| uuid = { version = "1.8.0", features = ["v4"] } | ||||
| httpdate = "1.0.3" | ||||
| zip = "4.3.0" | ||||
| mime_guess = "2.0.5" | ||||
| tempfile = "3.20.0" | ||||
| base64 = "0.22.1" | ||||
| zip = "2.2.0" | ||||
| mime_guess = "2.0.4" | ||||
| tempfile = "3.12.0" | ||||
| base64 = "0.22.0" | ||||
| ical = { version = "0.11.0", features = ["generator", "ical", "vcard"] } | ||||
| chrono = "0.4.42" | ||||
| chrono = "0.4.38" | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| use crate::app_config::AppConfig; | ||||
| use diesel::result::{DatabaseErrorKind, Error}; | ||||
| use diesel::{Connection, PgConnection}; | ||||
| use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; | ||||
| use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; | ||||
| use std::cell::RefCell; | ||||
|  | ||||
| const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); | ||||
| @@ -21,7 +21,7 @@ where | ||||
|     if POSTGRES_CONNECTION.with(|i| i.borrow().is_none()) { | ||||
|         let database_url = AppConfig::get().db_connection_chain(); | ||||
|         let conn = PgConnection::establish(&database_url) | ||||
|             .unwrap_or_else(|_| panic!("Error connecting to {database_url}")); | ||||
|             .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)); | ||||
|  | ||||
|         POSTGRES_CONNECTION.with(|i| *i.borrow_mut() = Some(conn)) | ||||
|     } | ||||
| @@ -38,7 +38,7 @@ where | ||||
|                 POSTGRES_CONNECTION.with(|i| *i.borrow_mut() = None) | ||||
|             } | ||||
|  | ||||
|             log::error!("Database query error! {e:?}"); | ||||
|             log::error!("Database query error! {:?}", e); | ||||
|             Err(e.into()) | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -30,7 +30,7 @@ pub async fn create_bucket_if_required() -> anyhow::Result<()> { | ||||
|             log::warn!("The bucket does not seem to exists, trying to create it!") | ||||
|         } | ||||
|         Err(e) => { | ||||
|             log::error!("Got unexpected error when querying bucket info: {e}"); | ||||
|             log::error!("Got unexpected error when querying bucket info: {}", e); | ||||
|             return Err(BucketServiceError::FailedFetchBucketInfo.into()); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use crate::extractors::accommodation_extractor::FamilyAndAccommodationInPath; | ||||
| use crate::extractors::family_extractor::{FamilyInPath, FamilyInPathWithAdminMembership}; | ||||
| use crate::models::Accommodation; | ||||
| use crate::services::accommodations_list_service; | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_web::{web, HttpResponse}; | ||||
|  | ||||
| #[derive(thiserror::Error, Debug)] | ||||
| enum AccommodationListControllerErr { | ||||
| @@ -34,16 +34,18 @@ impl AccommodationRequest { | ||||
|         } | ||||
|         accommodation.name = self.name; | ||||
|  | ||||
|         if let Some(d) = &self.description | ||||
|             && !c.accommodation_description_len.validate(d) { | ||||
|         if let Some(d) = &self.description { | ||||
|             if !c.accommodation_description_len.validate(d) { | ||||
|                 return Err(AccommodationListControllerErr::InvalidDescriptionLength.into()); | ||||
|             } | ||||
|         } | ||||
|         accommodation.description.clone_from(&self.description); | ||||
|  | ||||
|         if let Some(c) = &self.color | ||||
|             && !lazy_regex::regex!("[a-fA-F0-9]{6}").is_match(c) { | ||||
|         if let Some(c) = &self.color { | ||||
|             if !lazy_regex::regex!("[a-fA-F0-9]{6}").is_match(c) { | ||||
|                 return Err(AccommodationListControllerErr::MalformedColor.into()); | ||||
|             } | ||||
|         } | ||||
|         accommodation.color.clone_from(&self.color); | ||||
|  | ||||
|         accommodation.need_validation = self.need_validation; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use ical::{generator::*, *}; | ||||
|  | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_web::{web, HttpResponse}; | ||||
| use chrono::DateTime; | ||||
|  | ||||
| use crate::constants::StaticConstraints; | ||||
|   | ||||
| @@ -5,7 +5,7 @@ use crate::extractors::family_extractor::FamilyInPath; | ||||
| use crate::models::{Accommodation, AccommodationReservationID, NewAccommodationReservation}; | ||||
| use crate::services::accommodations_reservations_service; | ||||
| use crate::utils::time_utils::time; | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_web::{web, HttpResponse}; | ||||
|  | ||||
| #[derive(serde::Deserialize)] | ||||
| pub struct UpdateReservationQuery { | ||||
|   | ||||
| @@ -5,7 +5,7 @@ use crate::services::login_token_service::LoginTokenValue; | ||||
| use crate::services::rate_limiter_service::RatedAction; | ||||
| use crate::services::{login_token_service, openid_service, rate_limiter_service, users_service}; | ||||
| use actix_remote_ip::RemoteIP; | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_web::{web, HttpResponse}; | ||||
|  | ||||
| #[derive(serde::Deserialize)] | ||||
| pub struct CreateAccountBody { | ||||
| @@ -79,7 +79,10 @@ pub async fn request_reset_password( | ||||
|     match users_service::get_by_mail(&req.mail).await { | ||||
|         Ok(mut user) => users_service::request_reset_password(&mut user).await?, | ||||
|         Err(e) => { | ||||
|             log::error!("Could not locate user account {e}! (error silently ignored)"); | ||||
|             log::error!( | ||||
|                 "Could not locate user account {}! (error silently ignored)", | ||||
|                 e | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -119,7 +122,7 @@ pub async fn check_reset_password_token( | ||||
|                 RatedAction::CheckResetPasswordTokenFailed, | ||||
|             ) | ||||
|             .await?; | ||||
|             log::error!("Password reset token could not be used: {e}"); | ||||
|             log::error!("Password reset token could not be used: {}", e); | ||||
|             return Ok(HttpResponse::NotFound().finish()); | ||||
|         } | ||||
|     }; | ||||
| @@ -153,7 +156,7 @@ pub async fn reset_password(remote_ip: RemoteIP, req: web::Json<ResetPasswordBod | ||||
|                 RatedAction::CheckResetPasswordTokenFailed, | ||||
|             ) | ||||
|             .await?; | ||||
|             log::error!("Password reset token could not be used: {e}"); | ||||
|             log::error!("Password reset token could not be used: {}", e); | ||||
|             return Ok(HttpResponse::NotFound().finish()); | ||||
|         } | ||||
|     }; | ||||
| @@ -193,7 +196,7 @@ pub async fn password_login(remote_ip: RemoteIP, req: web::Json<PasswordLoginQue | ||||
|     let user = match users_service::get_by_mail(&req.mail).await { | ||||
|         Ok(u) => u, | ||||
|         Err(e) => { | ||||
|             log::error!("Auth failed: could not find account by mail! {e}"); | ||||
|             log::error!("Auth failed: could not find account by mail! {}", e); | ||||
|             rate_limiter_service::record_action(remote_ip.0, RatedAction::FailedPasswordLogin) | ||||
|                 .await?; | ||||
|             return Ok(HttpResponse::Unauthorized().json("Invalid credentials")); | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| use crate::controllers::HttpResult; | ||||
| use crate::controllers::members_controller::RequestDate; | ||||
| use crate::controllers::HttpResult; | ||||
| use crate::extractors::couple_extractor::FamilyAndCoupleInPath; | ||||
| use crate::extractors::family_extractor::FamilyInPath; | ||||
| use crate::models::{Couple, CoupleState, MemberID, PhotoID}; | ||||
| use crate::services::{couples_service, members_service, photos_service}; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_multipart::form::tempfile::TempFile; | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_web::{web, HttpResponse}; | ||||
|  | ||||
| serde_with::with_prefix!(prefix_wedding "wedding_"); | ||||
| serde_with::with_prefix!(prefix_divorce "divorce_"); | ||||
| @@ -48,20 +48,23 @@ impl CoupleRequest { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if let Some(husband) = self.husband | ||||
|             && !members_service::exists(couple.family_id(), husband).await? { | ||||
|         if let Some(husband) = self.husband { | ||||
|             if !members_service::exists(couple.family_id(), husband).await? { | ||||
|                 return Err(CoupleControllerErr::HusbandNotExisting.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if let Some(d) = &self.wedding | ||||
|             && !d.check() { | ||||
|         if let Some(d) = &self.wedding { | ||||
|             if !d.check() { | ||||
|                 return Err(CoupleControllerErr::MalformedDateOfWedding.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if let Some(d) = &self.divorce | ||||
|             && !d.check() { | ||||
|         if let Some(d) = &self.divorce { | ||||
|             if !d.check() { | ||||
|                 return Err(CoupleControllerErr::MalformedDateOfDivorce.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         couple.set_wife(self.wife); | ||||
|         couple.set_husband(self.husband); | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| use crate::connections::s3_connection; | ||||
| use crate::constants; | ||||
| use crate::controllers::HttpResult; | ||||
| use crate::controllers::couples_controller::CoupleRequest; | ||||
| use crate::controllers::members_controller::MemberRequest; | ||||
| use crate::controllers::HttpResult; | ||||
| use crate::extractors::family_extractor::{FamilyInPath, FamilyInPathWithAdminMembership}; | ||||
| use crate::models::{CoupleID, MemberID, PhotoID}; | ||||
| use crate::services::photos_service::UploadedFile; | ||||
| use crate::services::{couples_service, members_service, photos_service}; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_multipart::form::tempfile::TempFile; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_web::HttpResponse; | ||||
| use mime_guess::Mime; | ||||
| use std::collections::HashMap; | ||||
| @@ -183,9 +183,9 @@ pub async fn import_family( | ||||
|         } | ||||
|  | ||||
|         if let Err(e) = req_member_data.to_member(member).await { | ||||
|             log::error!("Error while processing import (member {req_id:?}) - {e}"); | ||||
|             log::error!("Error while processing import (member {:?}) - {e}", req_id); | ||||
|             return Ok( | ||||
|                 HttpResponse::BadRequest().json(format!("Failed to validate member {req_id:?}!")) | ||||
|                 HttpResponse::BadRequest().json(format!("Failed to validate member {:?}!", req_id)) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| use crate::constants::{FAMILY_INVITATION_CODE_LEN, StaticConstraints}; | ||||
| use crate::constants::{StaticConstraints, FAMILY_INVITATION_CODE_LEN}; | ||||
| use crate::controllers::HttpResult; | ||||
| use crate::extractors::family_extractor::{FamilyInPath, FamilyInPathWithAdminMembership}; | ||||
| use crate::models::{FamilyMembership, UserID}; | ||||
| @@ -7,7 +7,7 @@ use crate::services::rate_limiter_service::RatedAction; | ||||
| use crate::services::{families_service, rate_limiter_service}; | ||||
| use crate::utils::string_utils::rand_str; | ||||
| use actix_remote_ip::RemoteIP; | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_web::{web, HttpResponse}; | ||||
|  | ||||
| #[derive(Debug, serde::Deserialize)] | ||||
| pub struct CreateFamilyReq { | ||||
|   | ||||
| @@ -5,9 +5,9 @@ use crate::extractors::member_extractor::FamilyAndMemberInPath; | ||||
| use crate::models::{Member, MemberID, PhotoID, Sex}; | ||||
| use crate::services::{members_service, photos_service}; | ||||
| use crate::utils::countries_utils; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_multipart::form::tempfile::TempFile; | ||||
| use actix_web::{HttpResponse, web}; | ||||
| use actix_multipart::form::MultipartForm; | ||||
| use actix_web::{web, HttpResponse}; | ||||
|  | ||||
| serde_with::with_prefix!(prefix_birth "birth_"); | ||||
| serde_with::with_prefix!(prefix_death "death_"); | ||||
| @@ -95,10 +95,11 @@ fn check_opt_str_val( | ||||
|     c: SizeConstraint, | ||||
|     err: MemberControllerErr, | ||||
| ) -> anyhow::Result<()> { | ||||
|     if let Some(v) = val | ||||
|         && !c.validate(v) { | ||||
|     if let Some(v) = val { | ||||
|         if !c.validate(v) { | ||||
|             return Err(err.into()); | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @@ -150,10 +151,11 @@ impl MemberRequest { | ||||
|             MemberControllerErr::MalformedEmailAddress, | ||||
|         )?; | ||||
|  | ||||
|         if let Some(mail) = &self.email | ||||
|             && !mailchecker::is_valid(mail) { | ||||
|         if let Some(mail) = &self.email { | ||||
|             if !mailchecker::is_valid(mail) { | ||||
|                 return Err(MemberControllerErr::InvalidEmailAddress.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         check_opt_str_val( | ||||
|             &self.phone, | ||||
| @@ -185,20 +187,23 @@ impl MemberRequest { | ||||
|             MemberControllerErr::MalformedCountry, | ||||
|         )?; | ||||
|  | ||||
|         if let Some(c) = &self.country | ||||
|             && !countries_utils::is_code_valid(c) { | ||||
|         if let Some(c) = &self.country { | ||||
|             if !countries_utils::is_code_valid(c) { | ||||
|                 return Err(MemberControllerErr::InvalidCountryCode.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if let Some(d) = &self.birth | ||||
|             && !d.check() { | ||||
|         if let Some(d) = &self.birth { | ||||
|             if !d.check() { | ||||
|                 return Err(MemberControllerErr::MalformedDateOfBirth.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if let Some(d) = &self.death | ||||
|             && !d.check() { | ||||
|         if let Some(d) = &self.death { | ||||
|             if !d.check() { | ||||
|                 return Err(MemberControllerErr::MalformedDateOfDeath.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         check_opt_str_val( | ||||
|             &self.note, | ||||
| @@ -216,10 +221,11 @@ impl MemberRequest { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if let Some(father) = self.father | ||||
|             && !members_service::exists(member.family_id(), father).await? { | ||||
|         if let Some(father) = self.father { | ||||
|             if !members_service::exists(member.family_id(), father).await? { | ||||
|                 return Err(MemberControllerErr::FatherNotExisting.into()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         member.first_name = self.first_name; | ||||
|         member.last_name = self.last_name; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| //! # API controller | ||||
|  | ||||
| use actix_web::HttpResponse; | ||||
| use actix_web::body::BoxBody; | ||||
| use actix_web::HttpResponse; | ||||
| use std::fmt::{Debug, Display, Formatter}; | ||||
| use zip::result::ZipError; | ||||
|  | ||||
| @@ -31,7 +31,7 @@ impl Display for HttpErr { | ||||
|  | ||||
| impl actix_web::error::ResponseError for HttpErr { | ||||
|     fn error_response(&self) -> HttpResponse<BoxBody> { | ||||
|         log::error!("Error while processing request! {self}"); | ||||
|         log::error!("Error while processing request! {}", self); | ||||
|         HttpResponse::InternalServerError().body("Failed to execute request!") | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use crate::controllers::HttpResult; | ||||
| use crate::models::PhotoID; | ||||
| use crate::services::photos_service; | ||||
| use actix_web::http::header; | ||||
| use actix_web::{HttpRequest, HttpResponse, web}; | ||||
| use actix_web::{web, HttpRequest, HttpResponse}; | ||||
| use std::ops::Add; | ||||
| use std::time::{Duration, UNIX_EPOCH}; | ||||
|  | ||||
| @@ -36,15 +36,16 @@ async fn get_photo(id: &PhotoIdPath, full_size: bool, req: HttpRequest) -> HttpR | ||||
|     }; | ||||
|  | ||||
|     // Check if an upload is un-necessary | ||||
|     if let Some(c) = req.headers().get(header::IF_NONE_MATCH) | ||||
|         && c.to_str().unwrap_or("") == hash { | ||||
|     if let Some(c) = req.headers().get(header::IF_NONE_MATCH) { | ||||
|         if c.to_str().unwrap_or("") == hash { | ||||
|             return Ok(HttpResponse::NotModified().finish()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if let Some(c) = req.headers().get(header::IF_MODIFIED_SINCE) { | ||||
|         let date_str = c.to_str().unwrap_or(""); | ||||
|         if let Ok(date) = httpdate::parse_http_date(date_str) | ||||
|             && date | ||||
|         if let Ok(date) = httpdate::parse_http_date(date_str) { | ||||
|             if date | ||||
|                 .add(Duration::from_secs(1)) | ||||
|                 .duration_since(UNIX_EPOCH) | ||||
|                 .unwrap() | ||||
| @@ -53,6 +54,7 @@ async fn get_photo(id: &PhotoIdPath, full_size: bool, req: HttpRequest) -> HttpR | ||||
|             { | ||||
|                 return Ok(HttpResponse::NotModified().finish()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let bytes = s3_connection::get_file(&match full_size { | ||||
|   | ||||
| @@ -9,8 +9,8 @@ use crate::services::login_token_service::LoginToken; | ||||
| use crate::services::rate_limiter_service::RatedAction; | ||||
| use crate::services::{rate_limiter_service, users_service}; | ||||
| use actix_remote_ip::RemoteIP; | ||||
| use actix_web::HttpResponse; | ||||
| use actix_web::web::Json; | ||||
| use actix_web::HttpResponse; | ||||
|  | ||||
| #[derive(serde::Serialize)] | ||||
| struct UserAPI<'a> { | ||||
|   | ||||
| @@ -75,7 +75,7 @@ impl FromRequest for FamilyAndAccommodationInPath { | ||||
|             Self::load_accommodation_from_path(family, accommodation_id) | ||||
|                 .await | ||||
|                 .map_err(|e| { | ||||
|                     log::error!("Failed to extract accommodation ID from URL! {e}"); | ||||
|                     log::error!("Failed to extract accommodation ID from URL! {}", e); | ||||
|                     actix_web::error::ErrorNotFound("Could not fetch accommodation information!") | ||||
|                 }) | ||||
|         }) | ||||
|   | ||||
| @@ -85,7 +85,7 @@ impl FromRequest for FamilyAndAccommodationReservationCalendarInPath { | ||||
|             Self::load_calendar_from_path(family, accommodation_id) | ||||
|                 .await | ||||
|                 .map_err(|e| { | ||||
|                     log::error!("Failed to extract calendar ID from URL! {e}"); | ||||
|                     log::error!("Failed to extract calendar ID from URL! {}", e); | ||||
|                     actix_web::error::ErrorNotFound("Could not fetch calendar information!") | ||||
|                 }) | ||||
|         }) | ||||
|   | ||||
| @@ -95,7 +95,7 @@ impl FromRequest for FamilyAndAccommodationReservationInPath { | ||||
|             Self::load_accommodation_reservation_from_path(family, reservation_id) | ||||
|                 .await | ||||
|                 .map_err(|e| { | ||||
|                     log::error!("Failed to extract accommodation ID from URL! {e}"); | ||||
|                     log::error!("Failed to extract accommodation ID from URL! {}", e); | ||||
|                     actix_web::error::ErrorNotFound("Could not fetch accommodation information!") | ||||
|                 }) | ||||
|         }) | ||||
|   | ||||
| @@ -71,7 +71,7 @@ impl FromRequest for FamilyAndCoupleInPath { | ||||
|             FamilyAndCoupleInPath::load_couple_from_path(family, couple_id) | ||||
|                 .await | ||||
|                 .map_err(|e| { | ||||
|                     log::error!("Failed to extract couple ID from URL! {e}"); | ||||
|                     log::error!("Failed to extract couple ID from URL! {}", e); | ||||
|                     actix_web::error::ErrorNotFound("Could not fetch couple information!") | ||||
|                 }) | ||||
|         }) | ||||
|   | ||||
| @@ -62,7 +62,7 @@ impl FromRequest for FamilyInPath { | ||||
|             FamilyInPath::load_family_from_path(&token, family_id) | ||||
|                 .await | ||||
|                 .map_err(|e| { | ||||
|                     log::error!("Failed to extract family ID from URL! {e}"); | ||||
|                     log::error!("Failed to extract family ID from URL! {}", e); | ||||
|                     actix_web::error::ErrorNotFound("Could not fetch family information!") | ||||
|                 }) | ||||
|         }) | ||||
|   | ||||
| @@ -71,7 +71,7 @@ impl FromRequest for FamilyAndMemberInPath { | ||||
|             FamilyAndMemberInPath::load_member_from_path(family, member_id) | ||||
|                 .await | ||||
|                 .map_err(|e| { | ||||
|                     log::error!("Failed to extract member ID from URL! {e}"); | ||||
|                     log::error!("Failed to extract member ID from URL! {}", e); | ||||
|                     actix_web::error::ErrorNotFound("Could not fetch member information!") | ||||
|                 }) | ||||
|         }) | ||||
|   | ||||
| @@ -2,7 +2,7 @@ use actix_cors::Cors; | ||||
| use actix_multipart::form::tempfile::TempFileConfig; | ||||
| use actix_remote_ip::RemoteIPConfig; | ||||
| use actix_web::middleware::Logger; | ||||
| use actix_web::{App, HttpServer, web}; | ||||
| use actix_web::{web, App, HttpServer}; | ||||
| use geneit_backend::app_config::AppConfig; | ||||
| use geneit_backend::connections::{db_connection, s3_connection}; | ||||
| use geneit_backend::controllers::{ | ||||
|   | ||||
| @@ -43,7 +43,7 @@ impl User { | ||||
|             .as_deref() | ||||
|             .map(|hash| { | ||||
|                 bcrypt::verify(password, hash).unwrap_or_else(|e| { | ||||
|                     log::error!("Failed to validate password! {e}"); | ||||
|                     log::error!("Failed to validate password! {}", e); | ||||
|                     false | ||||
|                 }) | ||||
|             }) | ||||
|   | ||||
| @@ -9,7 +9,7 @@ use crate::utils::string_utils::rand_str; | ||||
| use crate::utils::time_utils::time; | ||||
| use actix_web::dev::Payload; | ||||
| use actix_web::{FromRequest, HttpRequest}; | ||||
| use std::future::{Ready, ready}; | ||||
| use std::future::{ready, Ready}; | ||||
|  | ||||
| #[derive(thiserror::Error, Debug)] | ||||
| enum LoginTokenServiceError { | ||||
| @@ -125,13 +125,13 @@ async fn load_token_info(token: &LoginTokenValue) -> anyhow::Result<Option<Login | ||||
|     let token = match user_tokens.iter_mut().find(|t| t.key == key) { | ||||
|         Some(t) => t, | ||||
|         None => { | ||||
|             log::error!("Could not find token for key '{key}' (missing token)"); | ||||
|             log::error!("Could not find token for key '{}' (missing token)", key); | ||||
|             return Ok(None); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     if token.is_expired() { | ||||
|         log::error!("Could not find token for key '{key}' (token expired)"); | ||||
|         log::error!("Could not find token for key '{}' (token expired)", key); | ||||
|         return Ok(None); | ||||
|     } | ||||
|  | ||||
| @@ -169,7 +169,7 @@ impl FromRequest for LoginToken { | ||||
|  | ||||
|             let token = match load_token_info(&token).await { | ||||
|                 Err(e) => { | ||||
|                     log::error!("Failed to load auth token! {e}"); | ||||
|                     log::error!("Failed to load auth token! {}", e); | ||||
|                     return Err(actix_web::error::ErrorPreconditionFailed( | ||||
|                         "Failed to check auth token!", | ||||
|                     )); | ||||
|   | ||||
| @@ -27,7 +27,7 @@ pub async fn send_mail<D: Display>(to: &str, subject: &str, body: D) -> anyhow:: | ||||
|     let mailer = mailer.build(); | ||||
|  | ||||
|     mailer.send(&email)?; | ||||
|     log::debug!("A mail was sent to {to} (subject = {subject})"); | ||||
|     log::debug!("A mail was sent to {} (subject = {})", to, subject); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,8 @@ use crate::models::{FamilyID, Member, MemberID, NewMember}; | ||||
| use crate::schema::members; | ||||
| use crate::services::{couples_service, photos_service}; | ||||
| use crate::utils::time_utils::time; | ||||
| use diesel::RunQueryDsl; | ||||
| use diesel::prelude::*; | ||||
| use diesel::RunQueryDsl; | ||||
|  | ||||
| /// Create a new family member | ||||
| pub async fn create(family_id: FamilyID) -> anyhow::Result<Member> { | ||||
| @@ -149,10 +149,11 @@ pub mod loop_detection { | ||||
|  | ||||
|     impl LoopStack<'_> { | ||||
|         pub fn contains(&self, id: MemberID) -> bool { | ||||
|             if let Some(ls) = &self.prev | ||||
|                 && ls.contains(id) { | ||||
|             if let Some(ls) = &self.prev { | ||||
|                 if ls.contains(id) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             self.curr == id | ||||
|         } | ||||
| @@ -167,7 +168,7 @@ pub mod loop_detection { | ||||
|             None => false, | ||||
|             Some(id) => { | ||||
|                 if curr_stack.contains(id) { | ||||
|                     log::debug!("Loop detected! {curr_stack:?}"); | ||||
|                     log::debug!("Loop detected! {:?}", curr_stack); | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
| @@ -187,10 +188,7 @@ pub mod loop_detection { | ||||
|         let member = match members.get(&curr_stack.curr) { | ||||
|             Some(m) => m, | ||||
|             None => { | ||||
|                 log::warn!( | ||||
|                     "Member {:?} not found in the tree for loop detection, this should never happen!", | ||||
|                     curr_stack.curr | ||||
|                 ); | ||||
|                 log::warn!("Member {:?} not found in the tree for loop detection, this should never happen!", curr_stack.curr); | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|   | ||||
| @@ -64,7 +64,7 @@ fn redis_key(state: &str) -> String { | ||||
|     format!("oidc-state-{state}") | ||||
| } | ||||
|  | ||||
| async fn load_provider_info(prov_id: &str) -> anyhow::Result<OpenIDClient<'_>> { | ||||
| async fn load_provider_info(prov_id: &str) -> anyhow::Result<OpenIDClient> { | ||||
|     let prov = AppConfig::get() | ||||
|         .openid_providers() | ||||
|         .into_iter() | ||||
|   | ||||
| @@ -6,8 +6,8 @@ use crate::utils::crypt_utils::sha512; | ||||
| use crate::utils::time_utils::time; | ||||
| use actix_multipart::form::tempfile::TempFile; | ||||
| use diesel::prelude::*; | ||||
| use image::ImageFormat; | ||||
| use image::imageops::FilterType; | ||||
| use image::ImageFormat; | ||||
| use mime_guess::Mime; | ||||
| use std::fs::File; | ||||
| use std::io::{Cursor, Read, Seek, Write}; | ||||
|   | ||||
| @@ -10,6 +10,7 @@ use crate::utils::string_utils::rand_str; | ||||
| use crate::utils::time_utils::time; | ||||
| use bcrypt::DEFAULT_COST; | ||||
| use diesel::prelude::*; | ||||
| use std::io::ErrorKind; | ||||
|  | ||||
| /// Get the information of a user, by its id | ||||
| pub async fn get_by_id(id: UserID) -> anyhow::Result<User> { | ||||
| @@ -24,7 +25,8 @@ pub async fn get_by_mail(mail: &str) -> anyhow::Result<User> { | ||||
| /// Get the information of a user, by its password reset token | ||||
| pub async fn get_by_pwd_reset_token(token: &str) -> anyhow::Result<User> { | ||||
|     if token.is_empty() { | ||||
|         return Err(anyhow::Error::from(std::io::Error::other( | ||||
|         return Err(anyhow::Error::from(std::io::Error::new( | ||||
|             ErrorKind::Other, | ||||
|             "Token is empty!", | ||||
|         ))); | ||||
|     } | ||||
| @@ -44,7 +46,8 @@ pub async fn get_by_pwd_reset_token(token: &str) -> anyhow::Result<User> { | ||||
| /// Get the information of a user, by its account deletion token | ||||
| pub async fn get_by_account_delete_token(token: &str) -> anyhow::Result<User> { | ||||
|     if token.is_empty() { | ||||
|         return Err(anyhow::Error::from(std::io::Error::other( | ||||
|         return Err(anyhow::Error::from(std::io::Error::new( | ||||
|             ErrorKind::Other, | ||||
|             "Token is empty!", | ||||
|         ))); | ||||
|     } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ pub fn sha256(bytes: &[u8]) -> String { | ||||
|     let mut hasher = Sha256::new(); | ||||
|     hasher.update(bytes); | ||||
|     let h = hasher.finalize(); | ||||
|     format!("{h:x}") | ||||
|     format!("{:x}", h) | ||||
| } | ||||
|  | ||||
| /// Compute hash of a slice of bytes (sha512) | ||||
| @@ -13,5 +13,5 @@ pub fn sha512(bytes: &[u8]) -> String { | ||||
|     let mut hasher = Sha512::new(); | ||||
|     hasher.update(bytes); | ||||
|     let h = hasher.finalize(); | ||||
|     format!("{h:x}") | ||||
|     format!("{:x}", h) | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| use base64::{Engine as _, engine::general_purpose}; | ||||
| use base64::{engine::general_purpose, Engine as _}; | ||||
|  | ||||
| /// Encode a base64 string | ||||
| pub fn base64_enc<T: AsRef<[u8]>>(b: T) -> String { | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| use rand::distr::{Alphanumeric, SampleString}; | ||||
| use rand::distributions::Alphanumeric; | ||||
| use rand::Rng; | ||||
|  | ||||
| /// Generate a random string of a given length | ||||
| /// Generate a random string of a given size | ||||
| pub fn rand_str(len: usize) -> String { | ||||
|     Alphanumeric.sample_string(&mut rand::rng(), len) | ||||
|     rand::thread_rng() | ||||
|         .sample_iter(&Alphanumeric) | ||||
|         .map(char::from) | ||||
|         .take(len) | ||||
|         .collect() | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,9 @@ | ||||
| { | ||||
|   "extends": ["local>renovate/presets"] | ||||
| } | ||||
|   "$schema": "https://docs.renovatebot.com/renovate-schema.json", | ||||
|   "packageRules": [ | ||||
|     { | ||||
|       "matchUpdateTypes": ["minor", "patch"], | ||||
|       "automerge": true | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user