Compare commits
1 Commits
master
...
567e0d2206
Author | SHA1 | Date | |
---|---|---|---|
567e0d2206 |
@ -14,7 +14,7 @@ steps:
|
||||
- cargo test
|
||||
|
||||
- name: app_deploy
|
||||
image: node:23
|
||||
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 },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
4795
geneit_app/package-lock.json
generated
4795
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",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@fontsource/roboto": "^5.2.6",
|
||||
"@fullcalendar/core": "^6.1.17",
|
||||
"@fullcalendar/daygrid": "^6.1.17",
|
||||
"@fullcalendar/interaction": "^6.1.17",
|
||||
"@fullcalendar/list": "^6.1.17",
|
||||
"@fullcalendar/react": "^6.1.17",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@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": "^7.1.2",
|
||||
"@mui/lab": "^7.0.0-beta.14",
|
||||
"@mui/material": "^7.1.2",
|
||||
"@mui/x-data-grid": "^7.29.6",
|
||||
"@mui/x-date-pickers": "^7.29.4",
|
||||
"@mui/x-tree-view": "^7.29.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": "^3.0.1",
|
||||
"mui-color-input": "^7.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-easy-crop": "^5.4.2",
|
||||
"react-qr-code": "^2.0.16",
|
||||
"react-router-dom": "^7.4.0",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"svg2pdf.js": "^2.5.0"
|
||||
"jspdf": "^2.5.2",
|
||||
"mui-color-input": "^4.0.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-easy-crop": "^5.0.8",
|
||||
"react-qr-code": "^2.0.14",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-zoom-pan-pinch": "^3.4.4",
|
||||
"svg2pdf.js": "^2.2.3",
|
||||
"typescript": "^5.6.2",
|
||||
"vite": "^5.4.8",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"web-vitals": "^3.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@types/react": "^19.0.12",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"eslint": "^9.29.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"globals": "^16.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.3.5"
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"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>
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
1486
geneit_backend/Cargo.lock
generated
1486
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.27"
|
||||
env_logger = "0.11.8"
|
||||
clap = { version = "4.5.40", features = ["derive", "env"] }
|
||||
lazy_static = "1.5.0"
|
||||
lazy-regex = "3.4.1"
|
||||
anyhow = "1.0.98"
|
||||
actix-web = "4.10.2"
|
||||
actix-cors = "0.7.1"
|
||||
actix-multipart = "0.7.2"
|
||||
log = "0.4.21"
|
||||
env_logger = "0.11.3"
|
||||
clap = { version = "4.5.4", features = ["derive", "env"] }
|
||||
lazy_static = "1.4.0"
|
||||
lazy-regex = "3.1.0"
|
||||
anyhow = "1.0.83"
|
||||
actix-web = "4.5.1"
|
||||
actix-cors = "0.7.0"
|
||||
actix-multipart = "0.6.1"
|
||||
actix-remote-ip = "0.1.0"
|
||||
futures-util = "0.3.31"
|
||||
diesel = { version = "2.2.11", features = ["postgres"] }
|
||||
diesel_migrations = "2.2.0"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
mailchecker = "6.0.17"
|
||||
redis = "0.29.5"
|
||||
lettre = "0.11.17"
|
||||
rand = "0.9.1"
|
||||
bcrypt = "0.17.0"
|
||||
light-openid = "1.0.4"
|
||||
thiserror = "2.0.12"
|
||||
serde_with = "3.12.0"
|
||||
rust_iso3166 = "0.1.14"
|
||||
rust-s3 = "0.35.1"
|
||||
sha2 = "0.10.9"
|
||||
image = "0.25.6"
|
||||
uuid = { version = "1.16.0", features = ["v4"] }
|
||||
futures-util = "0.3.30"
|
||||
diesel = { version = "2.1.6", features = ["postgres"] }
|
||||
diesel_migrations = "2.1.0"
|
||||
serde = { version = "1.0.198", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
mailchecker = "6.0.4"
|
||||
redis = "0.25.3"
|
||||
lettre = "0.11.7"
|
||||
rand = "0.8.5"
|
||||
bcrypt = "0.15.1"
|
||||
light-openid = "1.0.2"
|
||||
thiserror = "1.0.60"
|
||||
serde_with = "3.8.1"
|
||||
rust_iso3166 = "0.1.12"
|
||||
rust-s3 = "0.34.0"
|
||||
sha2 = "0.10.8"
|
||||
image = "0.25.1"
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
httpdate = "1.0.3"
|
||||
zip = "2.5.0"
|
||||
mime_guess = "2.0.5"
|
||||
tempfile = "3.19.1"
|
||||
base64 = "0.22.1"
|
||||
zip = "2.0.0"
|
||||
mime_guess = "2.0.4"
|
||||
tempfile = "3.10.1"
|
||||
base64 = "0.22.0"
|
||||
ical = { version = "0.11.0", features = ["generator", "ical", "vcard"] }
|
||||
chrono = "0.4.41"
|
||||
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 {
|
||||
|
@ -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_");
|
||||
|
@ -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_");
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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> {
|
||||
@ -147,7 +147,7 @@ pub mod loop_detection {
|
||||
prev: Option<&'a LoopStack<'a>>,
|
||||
}
|
||||
|
||||
impl LoopStack<'_> {
|
||||
impl<'a> LoopStack<'a> {
|
||||
pub fn contains(&self, id: MemberID) -> bool {
|
||||
if let Some(ls) = &self.prev {
|
||||
if ls.contains(id) {
|
||||
@ -168,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;
|
||||
}
|
||||
|
||||
@ -188,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;
|
||||
}
|
||||
};
|
||||
|
@ -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