Compare commits

..

No commits in common. "17b572441da4f18d422586a3467a4efdc742035a" and "45be22b0b9b33f84a78e0ffead297d9f84204259" have entirely different histories.

1506 changed files with 11964 additions and 64716 deletions

View file

@ -1,62 +1,80 @@
# ai-digest
.aidigestignore
# Ignore everything by default
*
# versioning
CHANGELOG.md
.versionrc.json
# First include the source directory
!src/
!src/**/
!src/**/*.js
!src/**/*.jsx
!src/**/*.ts
!src/**/*.tsx
# lint
.commitlintrc.json
.eslintrc.js
.editorconfig
.husky
# Then exclude specific patterns and directories
# Build and dependencies
node_modules/
build/
dist/
coverage/
# node
.yarnrc.yml
.yarn
# Platform specific
ios/
android/
# docker
.dockerignore
docker-compose.build.yaml
# Configuration files
*.json
*.lock
*.yml
*.yaml
*.env*
*.config.*
.*rc*
Dockerfile*
# env
.env.local
# Documentation
*.md
*.txt
docs/
# devbox
devbox.lock
.devbox/
# Tests and stories
**/__tests__/
**/*.test.*
**/*.spec.*
**/*.stories.*
e2e/
# git
.gitignore
# tmux
.tmux.conf
.tmuxp.full-decoupled.yaml
.tmuxp.yaml
# md
README.md
# ci/cd
.forgejo
# binaries
*.png
*.svg
*.jpg
*.jpeg
*.ico
# generic
*.bak
*.tmp
# Generated and utility files
*.d.ts
*.map
*.log
# project
tileserver-files
.dev-secrets
.osm-files
osm-files
tests
docs
dockerfile-x
# Assets and styles
src/assets/
**/*.css
**/*.scss
**/*.style.*
**/*.styles.*
*.svg
*.png
*.jpg
*.ttf
# Common utility/boilerplate directories
src/i18n/
src/theme/
src/utils/
src/lib/
src/components/
src/navigation/
src/hooks/
src/hoc/
# Auto-generated or index files
**/index.js
**/index.ts
**/constants.js
**/constants.ts
**/types.ts
# Deprecated files
*.bak
*.old

View file

@ -1,5 +0,0 @@
{
"extends": [
"@commitlint/config-conventional"
]
}

View file

@ -1,6 +0,0 @@
{
"crv": "Ed25519",
"d": "NuYqgJxxoFye_uIVcu1FYtpzeXhwCBcTP2od3xAQjFs",
"x": "BIoZ8WwlWgplcYSQKUvlzE9dE8jKEOOvH5_Z19N-2zc",
"kty": "OKP"
}

View file

@ -1,6 +0,0 @@
{
"crv": "Ed25519",
"d": "d3F9wKOVQIWXhDckflJs7KoUvI5mFX9jRCpeEbW4LG8",
"x": "H2MyHfvzAnNtnM6sUOhE32OIPPs-Ix6F13HaZ9fhCrg",
"kty": "OKP"
}

View file

@ -1,23 +1,66 @@
**/*.log
**/*.md
.git/
# OSX
#
**/.DS_Store
**/docker-compose.build.yaml
**/docker-compose.yaml
**/nodemon.json
**/.git
**/.dockerignore
**/Dockerfile
**/Dockerfile.*
**/*.dockerfile
**/node_modules
**/.eslintcache
**/.npm
/.osm-files
/osm-files
/tileserver-files
# Xcode
#
**/build/
**/*.pbxuser
!**/default.pbxuser
**/*.mode1v3
!**/default.mode1v3
**/*.mode2v3
!**/default.mode2v3
**/*.perspectivev3
!**/default.perspectivev3
**/xcuserdata
**/*.xccheckout
**/*.moved-aside
**/DerivedData
**/*.hmap
**/*.ipa
**/*.xcuserstate
**/project.xcworkspace
services/*/build
services/*/dist
# Android/IntelliJ
#
**/build/
**/.idea
**/.gradle
**/local.properties
**/*.iml
**/*.hprof
**/.cxx/
**/*.keystore
!**/debug.keystore
googleServiceAccountKey.json
# node.js
#
**/node_modules/
**/npm-debug.log
**/yarn-error.log
# Bundle artifacts
**/*.jsbundle
# CocoaPods
ios/Pods/
# Temporary files created by Metro to check the health of the file watcher
**/.metro-health-check*
# Expo
**/.expo/
**/web-build/
**/dist/
# Yarn berry
**/.pnp.*
**/.yarn/*
!**/.yarn/patches
!**/.yarn/plugins
!**/.yarn/releases
!**/.yarn/sdks
!**/.yarn/versions
# !.yarn/cache

View file

@ -1,10 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = false
charset = utf-8
[*.js]
indent_style = space
indent_size = 2

54
.env
View file

@ -1,54 +0,0 @@
# DEV PORTS
API_PORT=4200
WEB_PORT=4203
SERVICE_APP_PORT=4209
FILES_PORT=4292
EXPOSE_API_PORT=4200
EXPOSE_HASURA_PORT=4201
EXPOSE_PG_PORT=4204
EXPOSE_MINO_PORT=4290
EXPOSE_MINIO_PORT=4290
EXPOSE_MINO_CONSOLE_PORT=4291
EXPOSE_MINIO_CONSOLE_PORT=4291
MINIO_ROOT_USER=minio-admin
MINIO_ROOT_PASSWORD=minio-admin
EXPOSE_FILES_PORT=4292
EXPOSE_REDIS_QD_PORT=4278
EXPOSE_REDIS_HG_PORT=4279
EXPOSE_KVROCKS_CG_PORT=4277
EXPOSE_RABBITMQ_NODE_PORT=4272
EXPOSE_RABBITMQ_MANAGEMENT_PORT_PORT=4273
OSRM_CAR_PORT=4261
OSRM_FOOT_PORT=4262
OSRM_BICYCLE_PORT=4263
TILESERVERGL_PORT=4282
NOMINATIM_PORT=4283
HASURA_CONSOLE_PORT=4295
HASURA_CONSOLE_API_PORT=4293
# JWT
CLAIMS_NAMESPACE=https://alertesecours.fr/claims
# APP
APP_OA_FILES_URL=http://10.0.2.2:4292/api/v1/oas
APP_GRAPHQL_URL=http://10.0.2.2:4201/v1/graphql
APP_GRAPHQL_WS_URL=ws://10.0.2.2:4201/v1/graphql
APP_OSRM_CAR_URL=http://10.0.2.2:4261
APP_OSRM_FOOT_URL=http://10.0.2.2:4262
APP_OSRM_BICYCLE_URL=http://10.0.2.2:4263
APP_GEOLOC_SYNC_URL=http://10.0.2.2:4200/api/v1/oas/geoloc/sync
# APP_MAPVIEW_STYLE_URL=https://tiles.alertesecours.fr/styles/basic-preview/style.json
APP_MAPVIEW_STYLE_URL=http://10.0.2.2:4203/app/style.json
# APP_MAPVIEW_STYLE_URL=https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json
# API KEY
WHAT3WORDS_API_KEY=
# GoogleServices
ANDROID_GOOGLE_SERVICES_FILE_PATH=/home/jo/.lab/alertesecours/google-services.json
ANDROID_GOOGLE_SERVICES_FILE_PATH=/home/jo/.lab/alertesecours/GoogleService-Info.plist

View file

@ -1,54 +1,20 @@
# DEV PORTS
API_PORT=4200
WEB_PORT=4203
SERVICE_APP_PORT=4209
FILES_PORT=4292
# Sentry configuration
SENTRY_URL=https://sentry.io
SENTRY_DSN=your_sentry_dsn_here
SENTRY_ORG=your_sentry_org_here
EXPOSE_API_PORT=4200
EXPOSE_HASURA_PORT=4201
EXPOSE_PG_PORT=4204
EXPOSE_MINO_PORT=4290
EXPOSE_MINIO_PORT=4290
EXPOSE_MINO_CONSOLE_PORT=4291
EXPOSE_MINIO_CONSOLE_PORT=4291
MINIO_ROOT_USER=minio-admin
MINIO_ROOT_PASSWORD=minio-admin
EXPOSE_FILES_PORT=4292
EXPOSE_REDIS_QD_PORT=4278
EXPOSE_REDIS_HG_PORT=4279
EXPOSE_KVROCKS_CG_PORT=4277
SENTRY_PROJECT=alertesecours-application
SENTRY_DISABLE_AUTO_UPLOAD=true
LOCAL_DEV=true
EXPOSE_RABBITMQ_NODE_PORT=4272
EXPOSE_RABBITMQ_MANAGEMENT_PORT_PORT=4273
# Android Emulator Configuration
ANDROID_EMULATOR_NAME=Pixel_6_API_30
OSRM_CAR_PORT=4261
OSRM_FOOT_PORT=4262
OSRM_BICYCLE_PORT=4263
ASC_API_KEY_ID=
ASC_API_ISSUER_ID=
ASC_API_KEY_PATH=
PROVIDER_ID=
TILESERVERGL_PORT=4282
NOMINATIM_PORT=4283
HASURA_CONSOLE_PORT=4295
HASURA_CONSOLE_API_PORT=4293
# JWT
CLAIMS_NAMESPACE=https://alertesecours.fr/claims
# APP
APP_OA_FILES_URL=http://10.0.2.2:4292/api/v1/oas
APP_GRAPHQL_URL=http://10.0.2.2:4201/v1/graphql
APP_GRAPHQL_WS_URL=ws://10.0.2.2:4201/v1/graphql
APP_OSRM_CAR_URL=http://10.0.2.2:4261
APP_OSRM_FOOT_URL=http://10.0.2.2:4262
APP_OSRM_BICYCLE_URL=http://10.0.2.2:4263
APP_GEOLOC_SYNC_URL=http://10.0.2.2:4200/api/v1/oas/geoloc/sync
# APP_MAPVIEW_STYLE_URL=https://tiles.alertesecours.fr/styles/basic-preview/style.json
APP_MAPVIEW_STYLE_URL=http://10.0.2.2:4203/app/style.json
# APP_MAPVIEW_STYLE_URL=https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json
# API KEY
WHAT3WORDS_API_KEY=
# GoogleServices
ANDROID_GOOGLE_SERVICES_FILE_PATH=/home/jo/.lab/alertesecours/google-services.json
ANDROID_GOOGLE_SERVICES_FILE_PATH=/home/jo/.lab/alertesecours/GoogleService-Info.plist
# Background Geolocation License Keys
BACKGROUND_GEOLOCATION_LICENSE_ANDROID=your_license_key_here
BACKGROUND_GEOLOCATION_LICENSE_IOS=your_license_key_here

17
.envrc
View file

@ -1,2 +1,15 @@
# Automatically load the Devbox environment
eval "$(devbox generate direnv --print-envrc)"
export PATH=$PWD/bin:$PWD/scripts:$PATH
export PROJECT_WORKINGDIR=$PWD
# appli
export JAVA_HOME=${JAVA_HOME:-"/opt/android-studio/jbr"}
export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
export NODE_OPTIONS"=--openssl-legacy-provider"
# dotenv
dotenv_if_exists .env.default
dotenv_if_exists .env.local

View file

@ -1,111 +1,157 @@
const path = require("path")
const fs = require("fs")
// see https://github.com/import-js/eslint-plugin-import/issues/1174
const packageDirs = ["libs", "services"]
const packageDir = []
for (const dir of packageDirs) {
for (const d of fs
.readdirSync(path.resolve(__dirname, dir))
.filter(
(entry) =>
entry.slice(0, 1) !== "." &&
fs.lstatSync(path.resolve(__dirname, dir, entry)).isDirectory()
)) {
const p = path.resolve(dir, d)
packageDir.push(p)
}
}
const path = require("path");
module.exports = {
ignorePatterns: ["**/build/*", "modjo", "**/as-back"],
settings: {
"import/resolver": {
alias: true,
},
root: true,
env: {
"react-native/react-native": true,
jest: true,
},
extends: [
"airbnb-base",
"prettier",
"plugin:prettier/recommended",
"plugin:import/recommended",
"plugin:jsx-a11y/recommended",
"plugin:react-hooks/recommended",
],
plugins: ["sql-pretty"],
parser: "@babel/eslint-parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2021,
sourceType: "module",
babelOptions: {
configFile: path.resolve(__dirname, "babel.config.js"),
},
},
plugins: [
"babel",
"sort-keys-fix",
"react",
"react-native",
"react-native-a11y",
"unused-imports",
"autoimport-declarative",
],
settings: {
react: {
version: "detect",
},
"import/ignore": ["react-native"],
"import/resolver": {
// Ensure ESLint can resolve regular JS packages under Yarn PnP as well.
// Without this, some deps (ex: expo-sqlite) may be incorrectly flagged
// by import/no-unresolved even though they're present.
node: {
extensions: [".js", ".jsx", ".ts", ".tsx", ".json"],
},
typescript: {},
},
},
ignorePatterns: ["build", "node_modules", "e2e", "**/*.bak.js"],
rules: {
"no-undef": [2],
"sql-pretty/format": [1, { tags: ["sql"] }],
"no-shadow": [2, { allow: ["sql", "error"] }],
"node/no-extraneous-require": [0],
"import/no-commonjs": [0],
"import/no-dynamic-require": [0],
"import/no-extraneous-dependencies": [0],
"import/order": [
"error",
"react/forbid-prop-types": [0],
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react-hooks/exhaustive-deps": "error",
"jsx-a11y/no-autofocus": 0,
// React-Native accessibility: start as warnings to enable gradual adoption
// without breaking the build; we will tighten to errors as we fix surfaces.
"react-native-a11y/has-accessibility-hint": "error",
"react-native-a11y/has-valid-accessibility-descriptors": "error",
"import/no-named-as-default": 0,
"import/no-named-as-default-member": 0,
// 'unused-imports/no-unused-imports-ts': 1, # enable and run yarn lint --fix to autoremove all unused imports
"autoimport-declarative/autoimport": [
1,
{
groups: [
"builtin",
"external",
"internal",
"parent",
"index",
"sibling",
"object",
],
pathGroups: [
{
group: "internal",
pattern: "~/**",
},
{
group: "internal",
pattern: "~**",
},
],
pathGroupsExcludedImportTypes: [],
},
],
"global-require": [0],
"no-restricted-syntax": [0],
"no-async-promise-executor": [0],
"no-nested-ternary": [0],
"no-loop-func": [0],
"no-new": [0],
"func-names": [0],
"no-plusplus": [0],
"no-param-reassign": [0],
"no-continue": [0],
"no-unused-vars": [
2,
{
vars: "all",
args: "after-used",
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
"no-console": [0],
"no-throw-literal": [0],
"no-await-in-loop": [0],
"consistent-return": [0],
semi: ["error", "never"],
"prettier/prettier": [
"error",
{
semi: false,
},
],
},
parserOptions: {
ecmaVersion: "latest",
sourceType: "script",
env: [
{
node: true,
packages: {
react: [
"useState",
"useEffect",
"useContext",
"useReducer",
"useCallback",
"useMemo",
"useRef",
"useImperativeHandle",
"useLayoutEffect",
"useDebugValue",
"createRef",
"forwardRef",
{
name: "React",
isDefault: true,
},
],
"react-native": [
"useWindowDimensions",
"View",
"TouchableOpacity",
"TouchableHighlight",
"Image",
"StyleSheet",
],
"@react-navigation/native": [
"useNavigation",
"useFocusEffect",
"useIsFocused",
],
"@maplibre/maplibre-react-native": [
{
name: "Maplibre",
isDefault: true,
},
],
"@expo/vector-icons": [
"MaterialCommunityIcons",
"MaterialIcons",
"Entypo",
"FeatherMaterialIcons",
],
"react-native-paper": ["ToggleButton", "Button", "FAB"],
"@apollo/client": ["useQuery", "useMutation", "useLazyQuery"],
"react-hook-form": [
"FormProvider",
"Controller",
"useForm",
"useFormContext",
"useFieldArray",
"useField",
],
moment: [
{
name: "moment",
isDefault: true,
},
],
"hooks/useStreamQueryWithSubscription": [
{
name: "useStreamQueryWithSubscription",
isDefault: true,
},
],
"~/theme": ["useTheme", "createStyles"],
"~/lib/toast-notifications": ["useToast"],
"~/lib/geo/humanizeDistance": [
{
name: "humanizeDistance",
isDefault: true,
},
],
"~/components/Text": [
{
name: "Text",
isDefault: true,
},
],
},
},
],
},
globals: {
AggregateError: true,
dbug: true,
AbortController: true,
},
}
};

View file

@ -1,108 +0,0 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
on:
workflow_dispatch:
push:
branches:
- "main"
paths:
- "**"
- "!app/**"
tags:
- "**"
jobs:
build:
outputs:
tags: ${{ steps.meta.outputs.tags }}
runs-on: ubuntu-latest
container:
image: devthefuture/act-runner:latest
volumes:
- /buildkit-certs:/buildkit-certs
strategy:
matrix:
build:
- name: hasura
dockerfile: ./services/hasura/Dockerfile
- name: api
dockerfile: ./services/api/Dockerfile
- name: files
dockerfile: ./services/files/Dockerfile
- name: tasks
dockerfile: ./services/tasks/Dockerfile
- name: watchers
dockerfile: ./services/watchers/Dockerfile
- name: web
dockerfile: ./services/web/Dockerfile
- name: app
dockerfile: ./services/app/Dockerfile
steps:
- name: ⏬ Checkout code repository
uses: actions/checkout@v4
with:
token: ${{ secrets.M8A_ORG_BOT_REPO_TOKEN }} # Required for private repositories to works consistently, avoiding random errors
- name: 📌 Extract metadata (tags, labels) for Docker
id: meta
uses: https://git.devthefuture.org/devthefuture/docker-metadata-action@v5
with:
images: git.devthefuture.org/${{ github.repository }}/${{ matrix.build.name }}
tags: |
type=semver,pattern={{version}},priority=900
type=semver,pattern=v{{version}},priority=900
type=sha,priority=890
type=ref,event=branch,priority=600
type=ref,event=pr,priority=600
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }},priority=200
- name: 📦 Build and push Docker image
uses: https://git.devthefuture.org/devthefuture/actions/buildkit@main
with:
context: ${{ matrix.build.context || '.' }}
dockerfile: ${{ matrix.build.dockerfile }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
registry: git.devthefuture.org
registry-username: "org-bot-${{ github.repository_owner }}"
registry-password: ${{ secrets.M8A_ORG_BOT_PACKAGE_TOKEN }}
deploy:
needs: [build]
runs-on: ubuntu-latest
container:
image: devthefuture/act-runner:latest@sha256:f326ce2f586d4f55757b87d3de7baf29715ef6cbc5af7bdf6313bcf7a90e7b3d
steps:
- name: 🎡 Check out the Helm chart repository
uses: actions/checkout@v4
with:
repository: "${{ github.repository_owner }}/appsets"
token: ${{ secrets.M8A_ORG_BOT_REPO_TOKEN }}
ref: "main"
- name: 🚀 Upgrade images tag
uses: https://git.devthefuture.org/devthefuture/actions/uptag@v0.2.3
with:
app: |
---
name: hasura
key: graphql-engine.image.tag
---
name: api
key: modjo-microservice.image.tag
---
name: files
key: modjo-microservice.image.tag
---
name: tasks
key: modjo-microservice.image.tag
---
name: watchers
key: modjo-microservice.image.tag
---
name: app
key: nginx.image.tag
---
env: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'staging' }}
meta-tags: ${{ needs.build.outputs.tags }}
commit: "true"
push: "true"

113
.gitignore vendored
View file

@ -1,25 +1,66 @@
node_modules
.vscode/notes.txt
yarn-error.log
*.logs
# OSX
#
.DS_Store
.env.local
.env.yaml
.eslintcache
.devbox/
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
# project.xcworkspace
ios/AlerteSecours.xcodeproj/project.xcworkspace/**
!ios/AlerteSecours.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
# bunlde sourcemaps
ios/*.map
ios/*.hbc
.archive
/.osm-files
/osm-files
/tileserver-files
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
.cxx/
*.keystore
!debug.keystore
googleServiceAccountKey.json
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
services/*/build
services/*/dist
# Bundle artifacts
*.jsbundle
# CocoaPods
/ios/Pods/
# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*
# Expo
.expo/
web-build/
dist/
# Yarn berry
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
@ -28,6 +69,46 @@ services/*/dist
!.yarn/versions
# !.yarn/cache
# secrets
/keys
/.env.local
/.env.prod
/.env.staging
## Build generated
build/
DerivedData
/**/*.xcarchive/**
# aidigest
codebase.md
/bin
# Build logs
logs/
# Sensitive configuration files
ios/GoogleService-Info.plist
ios/AlerteSecours/GoogleService-Info.plist
ios/AlerteSecours/Supporting/Expo.plist
android/app/google-services.json
# Keep example files
!ios/GoogleService-Info.example.plist
!ios/AlerteSecours/GoogleService-Info.example.plist
!ios/AlerteSecours/Supporting/Expo.example.plist
!android/app/google-services.example.json
screenshot-*.png
/.data
# Geodae preprocessing
scripts/dae/node_modules/
scripts/dae/.yarn/*
!scripts/dae/.yarn/patches
!scripts/dae/.yarn/plugins
!scripts/dae/.yarn/releases
!scripts/dae/.yarn/sdks
!scripts/dae/.yarn/versions
src/assets/db/*.db

View file

@ -1,7 +0,0 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
export PATH=$PATH:$HOME/.yarn/bin
yarn commitlint --edit $1

View file

@ -1,6 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
export PATH=$PATH:$HOME/.yarn/bin
yarn lint-staged

View file

@ -1,9 +0,0 @@
**/node_modules/**
**/.git/**
**/.cache/**
**/.dev-secrets/**
**/android/**
**/ios/**
**/*.log
osm-files/**
tileserver-files/**

View file

@ -1,68 +0,0 @@
{
"bumpFiles": [
{
"filename": "package.json",
"type": "json"
},
{
"filename": "alerte-secours/package.json",
"type": "json"
},
{
"filename": "services/api/package.json",
"type": "json"
},
{
"filename": "services/tasks/package.json",
"type": "json"
},
{
"filename": "services/watchers/package.json",
"type": "json"
},
{
"filename": "services/web/package.json",
"type": "json"
},
{
"filename": "services/hasura/version.json",
"type": "json"
}
],
"types": [
{
"type": "feat",
"section": "Features"
},
{
"type": "fix",
"section": "Bug Fixes"
},
{
"type": "chore",
"hidden": true
},
{
"type": "docs",
"hidden": true
},
{
"type": "style",
"hidden": true
},
{
"type": "refactor",
"hidden": true
},
{
"type": "perf",
"hidden": true
},
{
"type": "test",
"hidden": true
}
],
"commitUrlFormat": "https://github.com/mokkapps/changelog-generator-demo/commits/{{hash}}",
"compareUrlFormat": "https://github.com/mokkapps/changelog-generator-demo/compare/{{previousTag}}...{{currentTag}}"
}

View file

@ -1,5 +0,0 @@
{
"recommendations": [
"dbaeumer.vscode-eslint"
]
}

27
.vscode/settings.json vendored
View file

@ -1,25 +1,6 @@
{
"path-autocomplete.pathMappings": {
"~": "${folder}/src"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript"
],
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"eslint.workingDirectories": [{ "mode": "auto" }],
"files.associations": {
".env.{default,local}": "dotenv",
"package.json.*": "json"
},
"javascript.suggest.autoImports": true,
"typescript.suggest.autoImports": true,
"editor.suggestSelection": "first",
"vsintellicode.modify.editor.suggestSelection": "automaticallyOverrodeDefaultValue",
"editor.inlineSuggest.enabled": true
"i18n-ally.localesPaths": [
"src/i18n",
"src/i18n/locales"
]
}

View file

@ -3,17 +3,14 @@
module.exports = {
name: "@yarnpkg/plugin-fetch",
factory: function (require) {
var plugin=(()=>{var le=Object.defineProperty;var pe=(s,e,t)=>e in s?le(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var c=(s=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(s,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):s)(function(s){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+s+'" is not supported')});var i=(s,e)=>()=>(e||s((e={exports:{}}).exports,e),e.exports);var f=(s,e,t)=>(pe(s,typeof e!="symbol"?e+"":e,t),t);var $=i((Ue,A)=>{"use strict";A.exports=function(e){return e.map(function(t){return t&&typeof t=="object"?t.op.replace(/(.)/g,"\\$1"):/["\s]/.test(t)&&!/'/.test(t)?"'"+t.replace(/(['\\])/g,"\\$1")+"'":/["'\s]/.test(t)?'"'+t.replace(/(["\\$`!])/g,"\\$1")+'"':String(t).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g,"$1\\$2")}).join(" ")}});var H=i((Ve,v)=>{var C=c("fs"),ue=c("path"),{parseSyml:fe}=c("@yarnpkg/parsers"),ke=["patch","npm","portal","link"],B=s=>{let e=s.trim().split("@");return s.startsWith("@")?e=e.slice(0,2):e=e.slice(0,1),e.join("@")},Y=(s,e)=>{let[t,r]=s.trim().split(`@${e}:`);return{key:t,version:r}},de=s=>{let[,e]=s.split("::")[0].split("#");return e};v.exports=function(){let e=C.readFileSync("yarn.lock","utf8"),t=fe(e),r=Object.keys(t).filter(o=>o.includes("@workspace:")),n=r.map(o=>{let[,l]=t[o].resolution.trim().split("@workspace:");return l==="."?null:l}).filter(Boolean);r.forEach(o=>{let{dependencies:l,dependenciesMeta:u,peerDependencies:b,peerDependenciesMeta:P,resolution:D,bin:ce}=t[o],[ae,w]=D.trim().split("@workspace:"),ie=ue.join(w,"package.json"),y={name:ae,version:"0.0.0",description:"**DON'T COMMIT** Generated file for caching",private:!0,dependencies:l,peerDependencies:b,peerDependenciesMeta:P,bin:ce};if(u){let m={};Object.keys(u).forEach(d=>{m[d]=l[d],delete l[d]}),y.optionalDependencies=m}if(w==="."){n.length>0&&(y.workspaces={packages:n});let m=Object.keys(t),d=new Map;m.forEach(p=>{p.split(",").forEach(k=>{if(k.includes("builtin<compat/"))return;let a=B(k);d.has(a)||d.set(a,[]),d.get(a).push(k)})}),y.resolutions=m.filter(p=>{var a;if(p.includes("@workspace:"))return!1;if(p.includes("@patch:"))return!((a=de(Y(p,"patch").version).match(/(\.\.\/)+/))!=null&&a.length);if(p.includes(", "))return!1;let k=B(p);return d.get(k).length===1}).reduce((p,k)=>(ke.forEach(a=>{if(!k.includes(`@${a}:`))return;let{key:x,version:g}=Y(k,a);switch(a){case"npm":p[x]=g.includes("@")?`${a}:${g}`:g;break;case"patch":k.includes("builtin<compat/")||(p[x]=`${a}:${g.split("::")[0]}`);break;case"portal":case"link":p[x]=`${a}:${g.split("::")[0]}`;break}}),p),{})}C.mkdirSync(w,{recursive:!0}),C.writeFileSync(ie,`${JSON.stringify(y,null,2)}
`)})}});var j=i((Ze,R)=>{var he=H();R.exports=s=>{s.context.stdout.write(`[YARN-FETCH] extracting package.json file(s) from yarn.lock
`),he()}});var K=i((Xe,L)=>{var h=c("fs"),me=c("path"),{execSync:ge}=c("child_process"),{parseSyml:ye}=c("@yarnpkg/parsers"),{BaseCommand:xe}=c("@yarnpkg/cli"),{Command:qe,Option:M}=c("clipanion"),be=$(),Pe=j(),q;L.exports=(q=class extends xe{protectPackageJson=M.Boolean("--protect-package-json");args=M.Proxy();async execute(){let{protectPackageJson:e=process.stdout.isTTY}=this,t=[];if(e){this.context.stdout.write(`[YARN-FETCH] backup possible package.json file(s)
`);let n=h.readFileSync("yarn.lock","utf8"),o=ye(n);t=Object.keys(o).filter(u=>u.includes("@workspace:")).map(u=>{let{resolution:b}=o[u],[,P]=b.trim().split("@workspace:");return me.join(P,"package.json")}),t.forEach(u=>{h.existsSync(u)&&!h.existsSync(`${u}.yarn-plugin-fetch-bak`)&&h.copyFileSync(u,`${u}.yarn-plugin-fetch-bak`)})}Pe(this);let r=`yarn ${be(this.args)}`;this.context.stdout.write(`[YARN-FETCH] ${r}
`);try{ge(r,{stdio:"inherit"})}catch(n){throw n}finally{e&&(this.context.stdout.write(`[YARN-FETCH] restoring possible package.json file(s)
`),t.forEach(n=>{h.existsSync(`${n}.yarn-plugin-fetch-bak`)?h.renameSync(`${n}.yarn-plugin-fetch-bak`,n):h.unlinkSync(n)}))}}},f(q,"paths",[["fetch"]]),f(q,"usage",qe.Usage({description:"fetch dependencies from yarn.lock in Docker build",details:`
var plugin=(()=>{var M=Object.defineProperty;var R=(n,t,e)=>t in n?M(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var o=(n=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(n,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):n)(function(n){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+n+'" is not supported')});var f=(n,t)=>()=>(t||n((t={exports:{}}).exports,t),t.exports);var g=(n,t,e)=>(R(n,typeof t!="symbol"?t+"":t,e),e);var P=f((ce,F)=>{"use strict";F.exports=function(t){return t.map(function(e){return e&&typeof e=="object"?e.op.replace(/(.)/g,"\\$1"):/["\s]/.test(e)&&!/'/.test(e)?"'"+e.replace(/(['\\])/g,"\\$1")+"'":/["'\s]/.test(e)?'"'+e.replace(/(["\\$`!])/g,"\\$1")+'"':String(e).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g,"$1\\$2")}).join(" ")}});var E=f((oe,J)=>{var j=o("fs"),z=o("path"),{parseSyml:G}=o("@yarnpkg/parsers");J.exports=function(){let t=j.readFileSync("yarn.lock","utf8"),e=G(t),d=Object.keys(e).filter(i=>i.includes("@workspace:")),s=d.map(i=>{let[,p]=e[i].resolution.trim().split("@workspace:");return p==="."?null:p}).filter(Boolean);d.forEach(i=>{let{dependencies:p,dependenciesMeta:r,peerDependencies:q,peerDependenciesMeta:w,resolution:C,bin:Y}=e[i],[B,b]=C.trim().split("@workspace:"),H=z.join(b,"package.json"),h={name:B,version:"0.0.0",description:"**DON'T COMMIT** Generated file for caching",private:!0,dependencies:p,peerDependencies:q,peerDependenciesMeta:w,bin:Y};if(r){let m={};Object.keys(r).forEach(k=>{m[k]=p[k],delete p[k]}),h.optionalDependencies=m}if(b==="."){s.length>0&&(h.workspaces={packages:s});let m=Object.keys(e),k=c=>{let a=c.trim().split("@");return c.startsWith("@")?a=a.slice(0,2):a=a.slice(0,1),a.join("@")};h.resolutions=m.filter(c=>{if(c.includes("@workspace:")||c.includes(", ")||!c.includes("@npm:"))return!1;let a=k(c);return m.every(y=>c===y?!0:y.split(",").map(l=>k(l)).every(l=>l!==a))}).reduce((c,a)=>{let[y,l]=a.trim().split("@npm:");return c[y]=l.includes("@")?`npm:${l}`:l,c},{})}j.mkdirSync(b,{recursive:!0}),j.writeFileSync(H,`${JSON.stringify(h,null,2)}
`)})}});var S=f((ae,T)=>{var I=E();T.exports=n=>{n.context.stdout.write(`[YARN-FETCH] extracting package.json file(s) from yarn.lock
`),I()}});var O=f((pe,N)=>{var u=o("fs"),K=o("path"),{execSync:U}=o("child_process"),{parseSyml:W}=o("@yarnpkg/parsers"),{BaseCommand:Z}=o("@yarnpkg/cli"),{Command:Q,Option:D}=o("clipanion"),V=P(),X=S(),x;N.exports=(x=class extends Z{protectPackageJson=D.Boolean("--protect-package-json");args=D.Proxy();async execute(){let{protectPackageJson:t=process.stdout.isTTY}=this,e=[];if(t){this.context.stdout.write(`[YARN-FETCH] backup possible package.json file(s)
`);let s=u.readFileSync("yarn.lock","utf8"),i=W(s);e=Object.keys(i).filter(r=>r.includes("@workspace:")).map(r=>{let{resolution:q}=i[r],[,w]=q.trim().split("@workspace:");return K.join(w,"package.json")}),e.forEach(r=>{u.existsSync(r)&&!u.existsSync(`${r}.yarn-plugin-fetch-bak`)&&u.copyFileSync(r,`${r}.yarn-plugin-fetch-bak`)})}X(this);let d=`yarn ${V(this.args)}`;this.context.stdout.write(`[YARN-FETCH] ${d}
`);try{U(d,{stdio:"inherit"})}catch(s){throw s}finally{t&&(this.context.stdout.write(`[YARN-FETCH] restoring possible package.json file(s)
`),e.forEach(s=>{u.existsSync(`${s}.yarn-plugin-fetch-bak`)?u.renameSync(`${s}.yarn-plugin-fetch-bak`,s):u.unlinkSync(s)}))}}},g(x,"paths",[["fetch"]]),g(x,"usage",Q.Usage({description:"fetch dependencies from yarn.lock in Docker build",details:`
expand yarn.lock to package.json file(s) and install dependencies in Docker build.
`,examples:[["yarn fetch --immutable","yarn fetch workspace my-package focus"]]})),q)});var W=i((st,I)=>{var{BaseCommand:we}=c("@yarnpkg/cli"),$e=j(),S;I.exports=(S=class extends we{async execute(){$e(this)}},f(S,"paths",[["fetch-tools","expand-lock"]]),S)});var z=i((rt,_)=>{function Ce(s,e,t){let r=e.split("."),n=s;for(let o of r){if(n[o]===void 0)return t;n=n[o]}return n}function je(s,e,t){let r=e.split("."),n=s;for(let o=0;o<r.length-1;o++){let l=r[o];(!n[l]||typeof n[l]!="object")&&(n[l]={}),n=n[l]}return n[r[r.length-1]]=t,s}function Se(s,e){let t=e.split("."),r=s;for(let n=0;n<t.length-1;n++){let o=t[n];if(!r[o])return!1;r=r[o]}return delete r[t[t.length-1]],!0}_.exports={get:Ce,set:je,unset:Se}});var E=i((ot,U)=>{var G=c("fs"),{get:Ee,set:Fe,unset:Je}=z();U.exports=function(e,t){let r=JSON.parse(G.readFileSync("package.json","utf-8")),n=Ee(r,e);n!==void 0&&(Fe(r,t,n),Je(r,e),G.writeFileSync("package.json",JSON.stringify(r,null,2)))}});var F=i((ct,V)=>{var Ne=E();V.exports=function(){Ne("scripts._postinstall","scripts.postinstall")}});var Q=i((it,Z)=>{var{BaseCommand:Te}=c("@yarnpkg/cli"),Oe=F(),J;Z.exports=(J=class extends Te{async execute(){Oe()}},f(J,"paths",[["fetch-tools","disable-postinstall"]]),J)});var N=i((pt,X)=>{var De=E();X.exports=function(){De("scripts.postinstall","scripts._postinstall")}});var te=i((ft,ee)=>{var{BaseCommand:Ae}=c("@yarnpkg/cli"),Be=N(),T;ee.exports=(T=class extends Ae{async execute(){Be()}},f(T,"paths",[["fetch-tools","disable-postinstall"]]),T)});var re=i((ht,ne)=>{var{execSync:Ye}=c("child_process"),{BaseCommand:ve}=c("@yarnpkg/cli"),{Option:se}=c("clipanion"),He=$(),Re=F(),Me=N(),O;ne.exports=(O=class extends ve{postinstall=se.Boolean("--postinstall");args=se.Proxy();async execute(){this.postinstall||(this.context.stdout.write(`[YARN-FETCH] disable postinstall command in package.json
`),Me());let e=`yarn workspaces focus --production ${He(this.args)}`;this.context.stdout.write(`[YARN-FETCH] ${e}
`),Ye(e,{stdio:"inherit"}),this.postinstall||(this.context.stdout.write(`[YARN-FETCH] re-enable postinstall command in package.json
`),Re())}},f(O,"paths",[["fetch-tools","production"]]),O)});var ze=i((gt,oe)=>{var Le=K(),Ke=W(),Ie=Q(),We=te(),_e=re();oe.exports={commands:[Le,Ke,We,Ie,_e]}});return ze();})();
`,examples:[["yarn fetch --immutable","yarn fetch workspace my-package focus"]]})),x)});var A=f((ke,v)=>{var{BaseCommand:_}=o("@yarnpkg/cli"),ee=S(),$;v.exports=($=class extends _{async execute(){ee(this)}},g($,"paths",[["expand-lock"]]),$)});var se=f((de,L)=>{var te=O(),ne=A();L.exports={commands:[te,ne]}});return se();})();
return plugin;
}
};

File diff suppressed because one or more lines are too long

View file

@ -5,8 +5,7 @@ enableGlobalCache: false
nodeLinker: node-modules
plugins:
- checksum: 240d225dd5bf1e25068497140ced7a3b7658a4c3754c08ea57162c9fe3335d757af0eae55555f96150a3015cdd0337852401f3fae69c1edd05221cb32f038d8c
path: .yarn/plugins/@yarnpkg/plugin-fetch.cjs
spec: "https://codeberg.org/devthefuture/yarn-plugin-fetch/raw/branch/master/bundles/@yarnpkg/plugin-fetch.js"
- path: .yarn/plugins/@yarnpkg/plugin-fetch.cjs
spec: "https://raw.githubusercontent.com/devthejo/yarn-plugin-fetch/master/bundles/@yarnpkg/plugin-fetch.js"
yarnPath: .yarn/releases/yarn-4.10.3.cjs
yarnPath: .yarn/releases/yarn-4.5.3.cjs

View file

@ -1,191 +1,266 @@
# Alerte-Secours Services - Developer Documentation
# Alerte Secours Mobile App - Developer Documentation
This document contains technical information for developers working on the Alerte-Secours microservices backend.
This document contains technical information for developers working on the Alerte Secours mobile application.
## Development Quick Start
## Table of Contents
### Requirements
- [Project Overview](#project-overview)
- [Technical Stack](#technical-stack)
- [Development Quick Start](#development-quick-start)
- [Installation](#installation)
- [Android](#android)
- [iOS](#ios)
- [Project Structure](#project-structure)
- [Accessibility](#accessibility)
- [Troubleshooting](#troubleshooting)
- Docker
- Devbox
- Android SDK (for mobile development)
- Java 8+ (for Android development)
## Project Overview
### Installation
Alerte Secours is a mobile application built with React Native that handles alerts and emergency-related functionality. The app supports both iOS and Android platforms and includes features such as:
1. Clone the repository:
```sh
git clone https://codeberg.org/alerte-secours/alerte-secours
cd alerte-secours
```
2. Install devbox if not already installed:
```sh
curl -fsSL https://get.jetpack.io/devbox | bash
```
3. Enter devbox shell (this will automatically install Node.js 20 and Yarn 4.6.0):
```sh
devbox shell
```
4. Install dependencies:
```sh
yarn
```
5. Set up environment variables:
```sh
cp .env.default .env
```
### Start Services
Start all services with Tilt:
```sh
yarn dev
# or
devbox run dev
# or run a subset of services:
tilt up api files hasura
```
View logs in Tilt UI (web HUD opens automatically). CLI alternative:
```sh
yarn dev:logs
# or
tilt logs
```
Stop services:
```sh
# If running in the foreground terminal:
Ctrl-C
# Or explicitly:
tilt down
```
Compose fallback (legacy):
```sh
yarn compose:up
yarn compose:down
# or via devbox:
devbox run compose:up
devbox run compose:down
```
### Using Devbox
Once you're in the devbox shell, you have access to:
- Node.js 20 (automatically installed)
- Yarn 4.6.0 (automatically installed)
- All project scripts and environment variables
- PostgreSQL client tools
To exit the devbox shell:
```sh
exit
```
To run commands from outside the shell:
```sh
devbox run <command>
```
Available devbox scripts:
- `devbox run dev` - Start development environment
- `devbox run dev:up` - Start all services
- `devbox run dev:logs` - View development logs
- `devbox run console` - Open development console
### Public Staging Environment
For development and testing, you can use our public staging environment:
- API: https://api-staging.alerte-secours.fr
- GraphQL: https://graphql-staging.alerte-secours.fr
- Web Interface: https://web-staging.alerte-secours.fr
### Endpoints (Local Development)
#### Services
- API: http://localhost:4200
- Files: http://localhost:4292
- Hasura: http://localhost:4201
- Tasks Service
- Watchers Service
- Web: http://localhost:4203
#### Consoles
- [Hasura Console](http://localhost:4295)
- [Minio Console](http://localhost:4291)
- [API Swagger](http://localhost:4200/api/v1/swagger/)
- [API GraphQL](http://localhost:4200/api/v1/graphql)
- [Files API](http://localhost:4292/api/v1/swagger/)
#### API URLs
- `/api/v1`
- `/spec` - API Specification
- `/oas` - OpenAPI Service
- `/swagger` - Swagger Documentation
- `/graphql` - GraphQL Endpoint
- `/status` - Service Status
- `/` - Root Endpoint
- Alert creation and management with real-time updates
- Location-based features with mapping integration
- Chat/Messaging system with alert-specific chat rooms
- Authentication via SMS verification
- Deep linking for alert sharing
- Push notifications
## Technical Stack
### Backend
- **Node.js** (>=20) - Core runtime
- **PostgreSQL** - Primary database
- **Redis** - Caching and queue deduplication
- **RabbitMQ** - Message queue
- **Hasura** - GraphQL engine
- **OpenAPI** - API specification and documentation
- **Modjo Framework** - Microservices architecture
- React Native
- Expo framework
- GraphQL with Hasura
- Apollo Client for frontend
- Federated remote schemas
- Real-time subscriptions support
- Firebase Cloud Messaging (FCM) for push notifications only
- Sentry for error tracking
- MapLibre for mapping functionality
- Zustand for state management
- i18next for internationalization
- React Navigation for navigation
- Expo Updates for OTA updates
- Background Geolocation for location tracking
- Lottie for animations
- React Hook Form for form handling
- Axios for HTTP requests
- Yarn Berry as package manager
- ESLint and Prettier for code quality
- Fastlane for deployment automation
### Frontend
- **React** - Web interface
- **React Native** - Mobile application
- **Apollo Client** - GraphQL integration
- **MapView** - Geolocation visualization
## Development Quick Start
### Infrastructure
- **Docker** - Containerization
- **Microservices Architecture** - Separate services for API, files, tasks, etc.
- **CQRS Pattern** - Command Query Responsibility Segregation
- **Event-Driven Architecture** - Using message queues
### Prerequisites
### Key Features
- Real-time alerts and notifications
- Geolocation tracking and mapping
- Emergency services integration
- User authentication and authorization
- File storage and management
- Background task processing
- Node.js (version specified in `.node-version`)
- Yarn package manager
- Android Studio (for Android development)
- Xcode (for iOS development)
- Physical device or emulator/simulator
### Environment Setup
1. Clone the repository
2. Install dependencies:
```bash
yarn
```
3. Copy the staging environment file:
```bash
cp .env.staging.example .env.staging
```
4. Start the development server with staging environment:
```bash
yarn start:staging
```
### Running on Devices
#### Android
```bash
yarn android:staging
```
#### iOS
```bash
yarn ios:staging
```
### Staging URLs
The staging environment uses the following URLs:
- GraphQL API: `https://hasura-staging.alertesecours.fr/v1/graphql`
- WebSocket: `wss://hasura-staging.alertesecours.fr/v1/graphql`
- Files API: `https://files-staging.alertesecours.fr/api/v1/oas`
- Minio: `https://minio-staging.alertesecours.fr`
- Geolocation Sync: `https://api-staging.alertesecours.fr/api/v1/oas/geoloc/sync`
## Installation
### Android
#### Using the Yarn Script
The easiest way to install the app is to use the provided yarn script:
```bash
# Set the device ID (emulator or physical device)
export DEVICE=emulator-5554
# Run the installation script
yarn install:android
```
This script (`install-android.sh`) handles the entire installation process, including building APKs with signing, extracting them, and installing on the device.
#### Manual Installation
If you need to install the app manually, you can examine the `install-android.sh` script in the project root to see the detailed steps involved.
### iOS
#### Authentication Key Setup
1. Go to https://appstoreconnect.apple.com/access/integrations/api
2. Click the "+" button to generate a new API key
3. Give it a name (e.g., "AlerteSecours Build Key")
4. Download the .p8 file when prompted
5. Store the .p8 file in a secure location
6. Note down the Key ID and Issuer ID shown on the website
7. Set up environment variables:
```sh
export ASC_API_KEY_ID="YOUR_KEY_ID"
export ASC_API_ISSUER_ID="YOUR_ISSUER_ID"
export ASC_API_KEY_PATH="/path/to/your/AuthKey.p8"
```
#### Building and Running
To build and run the iOS app:
```bash
# Run in development mode with staging environment
yarn ios:staging
# Build for production (version + clean + archive + export)
yarn bundle:ios:build
# Upload the last build to App Store Connect
yarn bundle:ios:upload
# Build + upload
yarn bundle:ios:release
```
Notes on versioning:
- `yarn bundle:ios:build` updates iOS `CFBundleShortVersionString` and `CFBundleVersion` to a timestamp in `Europe/Paris` timezone (format `YYYYMMDDHHMM`) before archiving/exporting.
- `yarn bundle:ios` is an alias of `yarn bundle:ios:build`.
The `bundle:ios` command uses the scripts in the `scripts` directory:
- `ios-archive.sh` - Archives the iOS app
- `ios-export.sh` - Exports the archived app
- `ios-upload.sh` - Uploads the app to App Store Connect (used by `bundle:ios:upload`)
## Project Structure
```
services/
├── api/ # Main API service
├── app/ # Frontend application
├── files/ # File handling service
├── hasura/ # GraphQL engine
├── tasks/ # Background task processing
├── watchers/ # Event monitoring
└── web/ # Web interface
libs/
├── common/ # Shared utilities
├── postgres-types/ # Database type definitions
├── redis-queue-dedup/ # Redis queue deduplication
└── utils/ # General utilities
```
- `/android` - Android-specific code and configuration
- `/ios` - iOS-specific code and configuration
- `/src` - Main application source code
- `/app` - App initialization and configuration
- `/assets` - Static assets (images, fonts, animations)
- `/auth` - Authentication-related code
- `/biz` - Business logic and constants
- `/components` - Reusable UI components
- `/containers` - Container components
- `/data` - Data management
- `/events` - Event handling
- `/finders` - Search and finder utilities
- `/gql` - GraphQL queries and mutations
- `/hoc` - Higher-order components
- `/hooks` - Custom React hooks
- `/i18n` - Internationalization
- `/layout` - Layout components
- `/lib` - Library code
- `/location` - Location-related functionality
- `/misc` - Miscellaneous utilities
- `/navigation` - Navigation configuration
- `/network` - Network-related code
- `/notifications` - Notification handling
- `/permissions` - Permission handling
- `/scenes` - Scene components
- `/screens` - Screen components
- `/sentry` - Sentry error tracking configuration
- `/stores` - State management stores
- `/theme` - Styling and theming
- `/updates` - Update handling
- `/utils` - Utility functions
- `/docs` - Documentation files
- `/scripts` - Utility scripts for building, deployment, and development
- `/e2e` - End-to-end tests
## Contributing
We welcome contributions to Alerte-Secours. Please read our contribution guidelines before submitting pull requests.
Guidelines for contributing to the project:
## Support
1. Follow the code style and conventions used in the project
2. Write tests for new features
3. Update documentation as needed
4. Use the ESLint and Prettier configurations
For technical support, please open an issue on our [Codeberg issue tracker](https://codeberg.org/alerte-secours/alerte-secours/-/issues) or [GitHub issue tracker](https://github.com/alerte-secours/alerte-secours/issues).
## Accessibility
This app has an accessibility baseline (WCAG 2.2 AA, VoiceOver/TalkBack) and app-specific conventions.
### Docs
- Baseline checklist: [`docs/a11y-wcag22-aa.md`](docs/a11y-wcag22-aa.md:1)
- Code conventions + helpers: [`docs/a11y-usage.md`](docs/a11y-usage.md:1)
- Color contrast guidance: [`docs/a11y-color-contrast.md`](docs/a11y-color-contrast.md:1)
- `testID` conventions: [`docs/testids.md`](docs/testids.md:1)
- QA runbook (iOS VoiceOver): [`docs/qa-voiceover.md`](docs/qa-voiceover.md:1)
- QA runbook (Android TalkBack): [`docs/qa-talkback.md`](docs/qa-talkback.md:1)
### PR checklist (required for any UI change)
- [ ] **Roles / labels / hints / states**: all interactive controls expose correct `accessibilityRole`, meaningful `accessibilityLabel`, helpful `accessibilityHint` (especially icon-only actions), and state where applicable.
- [ ] **Focus management**: any modal/dialog/sheet sets initial focus on open, returns focus on close, and avoids focus traps.
- [ ] **Touch target size**: critical tap targets are comfortably tappable (aim ~44x44pt minimum).
- [ ] **Color contrast**: text + icons meet WCAG AA contrast; do not rely on color alone for meaning.
- [ ] **`testID`s for critical controls**: stable `testID` added/updated for key actions and navigation chrome (per [`docs/testids.md`](docs/testids.md:1)).
- [ ] **Tests**: add/update tests covering the new UI behavior (and its states). Prefer assertions that dont depend on translated text; include E2E coverage for critical flows when applicable.
### Manual validation (screen readers)
When a PR changes UI or navigation, do a quick pass with the platform screen reader:
- iOS: follow [`docs/qa-voiceover.md`](docs/qa-voiceover.md:1) and validate labels/hints, navigation order, and activation behavior on the affected screens.
- Android: follow [`docs/qa-talkback.md`](docs/qa-talkback.md:1) with the same focus on discoverability, focus order, and activation.
## Troubleshooting
### Common Issues
#### Development Environment
- **Clearing Yarn Cache**: Use `yarn clean` (removes node_modules and reinstalls dependencies)
- **Cleaning Gradle**: Run `cd android && ./gradlew clean`
- **Clearing Gradle Cache**: Remove gradle caches with `rm -rf ~/.gradle/caches/ android/.gradle/`
- **Stopping Gradle Daemons**: Run `cd android && ./gradlew --stop`
- **Clearing ADB Cache**: Run `adb shell pm clear com.alertesecours`
- **Rebuilding Gradle**: Use `yarn expo run:android`
- **Rebuilding Expo React Native**: Use `yarn expo prebuild`
- **Clearing Metro Cache**: Use `yarn expo start --dev-client --clear`
#### Screenshots and Testing
- For Android screenshots: Use `scripts/screenshot-android.sh`
- For iOS screenshots: Use `scripts/screenshot-ios.sh`
- For Android emulator: Use `scripts/android-emulator`
#### Emulator Issues
- Clear cache / uninstall the app
- Check emulator datetime
- Check network connectivity
For more troubleshooting tips, see the documentation in the `/docs` directory.

View file

@ -1,38 +1,57 @@
# Alerte-Secours - Le Réflexe qui Sauve
# Alerte Secours - Le Réflexe qui Sauve
[![Liberapay](https://img.shields.io/liberapay/receives/alerte-secours.svg?logo=liberapay)](https://liberapay.com/alerte-secours)
[![Buy Me a Coffee](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?logo=buy-me-a-coffee)](https://buymeacoffee.com/alertesecours)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/alerte-secours?style=social)](https://github.com/sponsors/alerte-secours)
Alerte-Secours est un système d'alerte et de réponse d'urgence basé sur une architecture de microservices, conçu pour fournir une assistance rapide en situation d'urgence.
Une application mobile pour la gestion des alertes et des fonctionnalités liées aux urgences, supportant les plateformes iOS et Android.
## Site Web Officiel
**Site Web Officiel :** [alerte-secours.fr](https://alerte-secours.fr)
Visitez notre site web officiel sur [alerte-secours.fr](https://alerte-secours.fr)
## Aperçu du Projet
Alerte Secours est une application mobile construite avec React Native qui gère les alertes et les fonctionnalités liées aux urgences. L'application supporte les plateformes iOS et Android et inclut des fonctionnalités telles que :
- Création et gestion d'alertes avec mises à jour en temps réel
- Fonctionnalités basées sur la localisation avec intégration cartographique
- Système de chat/messagerie avec salles de discussion spécifiques aux alertes
- Authentification via vérification SMS
- Liens profonds pour le partage d'alertes
- Notifications push
## Documentation Développeur
Pour les développeurs souhaitant contribuer au projet ou déployer les services, consultez la [documentation technique complète](DEVELOPER.md) qui contient :
Pour les développeurs souhaitant contribuer au projet ou déployer l'application, consultez la [documentation technique complète](DEVELOPER.md) qui contient :
- Guide de démarrage rapide
- Aperçu du projet et fonctionnalités
- Stack technique détaillé
- Instructions d'installation
- Documentation des API
- Guide de démarrage rapide
- Instructions d'installation (Android/iOS)
- Structure du projet
- Endpoints de développement
- Guide de dépannage
- Instructions de build et déploiement
## Licence
Alerte-Secours est sous licence **DevTheFuture Ethical Use License (DEF License)**.
Alerte Secours est sous licence **DevTheFuture Ethical Use License (DEF License)**. Points clés :
Points clés :
- **Usage à but non lucratif** : Gratuit pour les usages à but non lucratif
- **Usage commercial** : Nécessite une licence payante
- **Données personnelles** : Ne doivent pas être monétisées ou exploitées
- **Propriété** : Tous les droits de propriété intellectuelle restent au concédant
- **Restriction concurrentielle** : Les concurrents doivent obtenir une autorisation explicite
### Usage à but non lucratif
- Licence perpétuelle, libre de redevances et non exclusive pour usage à but non lucratif
- Permet l'utilisation, la modification et la distribution à des fins non lucratives
Pour les détails complets de la licence, voir [LICENSE.md](LICENSE.md).
### Usage commercial
- Nécessite l'obtention d'une licence payante
- Conditions déterminées par le Concédant (DevTheFuture.org)
### Restrictions sur les données personnelles
- Ne doit pas être utilisé pour monétiser, vendre ou exploiter les données personnelles
- Les données personnelles ne peuvent pas être utilisées pour le marketing, la publicité ou l'influence politique
- L'agrégation de données n'est autorisée que si c'est une fonctionnalité explicite divulguée aux utilisateurs
### Restriction concurrentielle
- Les concurrents sont interdits d'utiliser le logiciel sans consentement explicite
Pour le texte complet de la licence, voir [LICENSE.md](LICENSE.md).
## 💙 Soutenir le projet
@ -51,8 +70,13 @@ Si vous souhaitez contribuer à son développement, sa maintenance et son indép
## Contribuer
Nous accueillons les contributions à Alerte-Secours. Veuillez lire nos directives de contribution avant de soumettre des pull requests.
Directives pour contribuer au projet :
1. Suivez le style de code et les conventions utilisées dans le projet
2. Écrivez des tests pour les nouvelles fonctionnalités
3. Mettez à jour la documentation si nécessaire
4. Utilisez les configurations ESLint et Prettier
## Support
Pour obtenir de l'aide, veuillez ouvrir un ticket sur notre [tracker d'issues Codeberg](https://codeberg.org/alerte-secours/alerte-secours/-/issues) ou [tracker d'issues GitHub](https://github.com/alerte-secours/alerte-secours/issues).
Pour obtenir de l'aide, veuillez ouvrir un ticket sur notre tracker d'issues ou consulter la documentation dans le répertoire `/docs`.

View file

@ -1,38 +0,0 @@
# Tiltfile for as-services
# Orchestrates docker-compose services with Tilt, providing a unified UI/logs and incremental workflows.
# Watch ignores are configured via .tiltignore
# Ensure .env exists so docker-compose gets expected env
local_resource(
name="ensure-env",
cmd="bash -lc 'if [ ! -f .env ] && [ -f .env.default ]; then cp .env.default .env && echo Created .env from .env.default; fi'",
allow_parallel=True,
auto_init=True,
)
# Optional: pre-pull builder image used in Dockerfiles
local_resource(
name="pull-builder",
cmd="docker pull devthefuture/dockerfile-x",
allow_parallel=True,
auto_init=True,
)
# Drive docker-compose with Tilt
dc = docker_compose('./docker-compose.yaml')
# Compose services are registered via docker_compose; explicit resource dependencies
# removed for compatibility with Tilt v0.35 (resource_deps not available).
# Optional groups for readability (requires Tilt Teams; keep commented if not used)
# set_team_ui_settings({
# 'resource_groups': {
# 'core': ['db', 'rabbitmq', 'redis-q-dedup', 'redis-hot-geodata', 'kvrocks-cold-geodata', 'maildev'],
# 'object-storage': ['minio', 'minio-setup'],
# 'hasura': ['hasura', 'hasura_console'],
# 'api-stack': ['api', 'files', 'tasks', 'watchers'],
# 'geo': ['osrm-car', 'osrm-foot', 'tileserver-gl', 'nominatim', 'nominatim-pg'],
# 'frontends': ['web', 'app'],
# }
# })

View file

Before

Width:  |  Height:  |  Size: 692 B

After

Width:  |  Height:  |  Size: 692 B

View file

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 903 B

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

Before

Width:  |  Height:  |  Size: 426 B

After

Width:  |  Height:  |  Size: 426 B

View file

Before

Width:  |  Height:  |  Size: 571 B

After

Width:  |  Height:  |  Size: 571 B

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View file

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

Before

Width:  |  Height:  |  Size: 985 B

After

Width:  |  Height:  |  Size: 985 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View file

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View file

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View file

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show more