diff --git a/.gitignore b/.gitignore index 00a350d..050d61a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ node_modules !.env.example # Uploads directory -public/ public/uploads/ assets/img/ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..d1a6e4f Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icons/android/android-launchericon-144-144.png b/public/icons/android/android-launchericon-144-144.png new file mode 100644 index 0000000..d1745df Binary files /dev/null and b/public/icons/android/android-launchericon-144-144.png differ diff --git a/public/icons/android/android-launchericon-192-192.png b/public/icons/android/android-launchericon-192-192.png new file mode 100644 index 0000000..c658e96 Binary files /dev/null and b/public/icons/android/android-launchericon-192-192.png differ diff --git a/public/icons/android/android-launchericon-48-48.png b/public/icons/android/android-launchericon-48-48.png new file mode 100644 index 0000000..94789a3 Binary files /dev/null and b/public/icons/android/android-launchericon-48-48.png differ diff --git a/public/icons/android/android-launchericon-512-512.png b/public/icons/android/android-launchericon-512-512.png new file mode 100644 index 0000000..0f32288 Binary files /dev/null and b/public/icons/android/android-launchericon-512-512.png differ diff --git a/public/icons/android/android-launchericon-72-72.png b/public/icons/android/android-launchericon-72-72.png new file mode 100644 index 0000000..874557d Binary files /dev/null and b/public/icons/android/android-launchericon-72-72.png differ diff --git a/public/icons/android/android-launchericon-96-96.png b/public/icons/android/android-launchericon-96-96.png new file mode 100644 index 0000000..33e573a Binary files /dev/null and b/public/icons/android/android-launchericon-96-96.png differ diff --git a/public/icons/ios/100.png b/public/icons/ios/100.png new file mode 100644 index 0000000..f72f31c Binary files /dev/null and b/public/icons/ios/100.png differ diff --git a/public/icons/ios/1024.png b/public/icons/ios/1024.png new file mode 100644 index 0000000..7316ff7 Binary files /dev/null and b/public/icons/ios/1024.png differ diff --git a/public/icons/ios/114.png b/public/icons/ios/114.png new file mode 100644 index 0000000..a58d8d8 Binary files /dev/null and b/public/icons/ios/114.png differ diff --git a/public/icons/ios/120.png b/public/icons/ios/120.png new file mode 100644 index 0000000..a624062 Binary files /dev/null and b/public/icons/ios/120.png differ diff --git a/public/icons/ios/128.png b/public/icons/ios/128.png new file mode 100644 index 0000000..8dc9761 Binary files /dev/null and b/public/icons/ios/128.png differ diff --git a/public/icons/ios/144.png b/public/icons/ios/144.png new file mode 100644 index 0000000..d1745df Binary files /dev/null and b/public/icons/ios/144.png differ diff --git a/public/icons/ios/152.png b/public/icons/ios/152.png new file mode 100644 index 0000000..8a8d828 Binary files /dev/null and b/public/icons/ios/152.png differ diff --git a/public/icons/ios/16.png b/public/icons/ios/16.png new file mode 100644 index 0000000..bf1e8dc Binary files /dev/null and b/public/icons/ios/16.png differ diff --git a/public/icons/ios/167.png b/public/icons/ios/167.png new file mode 100644 index 0000000..557eab1 Binary files /dev/null and b/public/icons/ios/167.png differ diff --git a/public/icons/ios/180.png b/public/icons/ios/180.png new file mode 100644 index 0000000..1221b67 Binary files /dev/null and b/public/icons/ios/180.png differ diff --git a/public/icons/ios/192.png b/public/icons/ios/192.png new file mode 100644 index 0000000..c658e96 Binary files /dev/null and b/public/icons/ios/192.png differ diff --git a/public/icons/ios/20.png b/public/icons/ios/20.png new file mode 100644 index 0000000..1e94680 Binary files /dev/null and b/public/icons/ios/20.png differ diff --git a/public/icons/ios/256.png b/public/icons/ios/256.png new file mode 100644 index 0000000..191ff36 Binary files /dev/null and b/public/icons/ios/256.png differ diff --git a/public/icons/ios/29.png b/public/icons/ios/29.png new file mode 100644 index 0000000..66f379c Binary files /dev/null and b/public/icons/ios/29.png differ diff --git a/public/icons/ios/32.png b/public/icons/ios/32.png new file mode 100644 index 0000000..eeeb3f8 Binary files /dev/null and b/public/icons/ios/32.png differ diff --git a/public/icons/ios/40.png b/public/icons/ios/40.png new file mode 100644 index 0000000..b26761b Binary files /dev/null and b/public/icons/ios/40.png differ diff --git a/public/icons/ios/50.png b/public/icons/ios/50.png new file mode 100644 index 0000000..555d6ed Binary files /dev/null and b/public/icons/ios/50.png differ diff --git a/public/icons/ios/512.png b/public/icons/ios/512.png new file mode 100644 index 0000000..0f32288 Binary files /dev/null and b/public/icons/ios/512.png differ diff --git a/public/icons/ios/57.png b/public/icons/ios/57.png new file mode 100644 index 0000000..a014f64 Binary files /dev/null and b/public/icons/ios/57.png differ diff --git a/public/icons/ios/58.png b/public/icons/ios/58.png new file mode 100644 index 0000000..abf29d3 Binary files /dev/null and b/public/icons/ios/58.png differ diff --git a/public/icons/ios/60.png b/public/icons/ios/60.png new file mode 100644 index 0000000..972dfb1 Binary files /dev/null and b/public/icons/ios/60.png differ diff --git a/public/icons/ios/64.png b/public/icons/ios/64.png new file mode 100644 index 0000000..5d3034c Binary files /dev/null and b/public/icons/ios/64.png differ diff --git a/public/icons/ios/72.png b/public/icons/ios/72.png new file mode 100644 index 0000000..874557d Binary files /dev/null and b/public/icons/ios/72.png differ diff --git a/public/icons/ios/76.png b/public/icons/ios/76.png new file mode 100644 index 0000000..9f0b0cf Binary files /dev/null and b/public/icons/ios/76.png differ diff --git a/public/icons/ios/80.png b/public/icons/ios/80.png new file mode 100644 index 0000000..c65629c Binary files /dev/null and b/public/icons/ios/80.png differ diff --git a/public/icons/ios/87.png b/public/icons/ios/87.png new file mode 100644 index 0000000..f6e49b3 Binary files /dev/null and b/public/icons/ios/87.png differ diff --git a/public/icons/windows11/LargeTile.scale-100.png b/public/icons/windows11/LargeTile.scale-100.png new file mode 100644 index 0000000..a8fa884 Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-100.png differ diff --git a/public/icons/windows11/LargeTile.scale-125.png b/public/icons/windows11/LargeTile.scale-125.png new file mode 100644 index 0000000..bf7b6a7 Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-125.png differ diff --git a/public/icons/windows11/LargeTile.scale-150.png b/public/icons/windows11/LargeTile.scale-150.png new file mode 100644 index 0000000..e539dec Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-150.png differ diff --git a/public/icons/windows11/LargeTile.scale-200.png b/public/icons/windows11/LargeTile.scale-200.png new file mode 100644 index 0000000..9014e81 Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-200.png differ diff --git a/public/icons/windows11/LargeTile.scale-400.png b/public/icons/windows11/LargeTile.scale-400.png new file mode 100644 index 0000000..4a8f5f3 Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-400.png differ diff --git a/public/icons/windows11/SmallTile.scale-100.png b/public/icons/windows11/SmallTile.scale-100.png new file mode 100644 index 0000000..f7fafd3 Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-100.png differ diff --git a/public/icons/windows11/SmallTile.scale-125.png b/public/icons/windows11/SmallTile.scale-125.png new file mode 100644 index 0000000..601b774 Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-125.png differ diff --git a/public/icons/windows11/SmallTile.scale-150.png b/public/icons/windows11/SmallTile.scale-150.png new file mode 100644 index 0000000..9b3edad Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-150.png differ diff --git a/public/icons/windows11/SmallTile.scale-200.png b/public/icons/windows11/SmallTile.scale-200.png new file mode 100644 index 0000000..f28a5e2 Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-200.png differ diff --git a/public/icons/windows11/SmallTile.scale-400.png b/public/icons/windows11/SmallTile.scale-400.png new file mode 100644 index 0000000..b94a642 Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-400.png differ diff --git a/public/icons/windows11/SplashScreen.scale-100.png b/public/icons/windows11/SplashScreen.scale-100.png new file mode 100644 index 0000000..5f4626d Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-100.png differ diff --git a/public/icons/windows11/SplashScreen.scale-125.png b/public/icons/windows11/SplashScreen.scale-125.png new file mode 100644 index 0000000..a1e00a6 Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-125.png differ diff --git a/public/icons/windows11/SplashScreen.scale-150.png b/public/icons/windows11/SplashScreen.scale-150.png new file mode 100644 index 0000000..1a8c6b6 Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-150.png differ diff --git a/public/icons/windows11/SplashScreen.scale-200.png b/public/icons/windows11/SplashScreen.scale-200.png new file mode 100644 index 0000000..741809a Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-200.png differ diff --git a/public/icons/windows11/SplashScreen.scale-400.png b/public/icons/windows11/SplashScreen.scale-400.png new file mode 100644 index 0000000..dd84d5b Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-400.png differ diff --git a/public/icons/windows11/Square150x150Logo.scale-100.png b/public/icons/windows11/Square150x150Logo.scale-100.png new file mode 100644 index 0000000..71ac343 Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-100.png differ diff --git a/public/icons/windows11/Square150x150Logo.scale-125.png b/public/icons/windows11/Square150x150Logo.scale-125.png new file mode 100644 index 0000000..4f6ee48 Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-125.png differ diff --git a/public/icons/windows11/Square150x150Logo.scale-150.png b/public/icons/windows11/Square150x150Logo.scale-150.png new file mode 100644 index 0000000..f4944fa Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-150.png differ diff --git a/public/icons/windows11/Square150x150Logo.scale-200.png b/public/icons/windows11/Square150x150Logo.scale-200.png new file mode 100644 index 0000000..26d3bc0 Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-200.png differ diff --git a/public/icons/windows11/Square150x150Logo.scale-400.png b/public/icons/windows11/Square150x150Logo.scale-400.png new file mode 100644 index 0000000..1398d62 Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-400.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png new file mode 100644 index 0000000..367fc8d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png new file mode 100644 index 0000000..56f7b65 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png new file mode 100644 index 0000000..13a7348 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png new file mode 100644 index 0000000..53b4463 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png new file mode 100644 index 0000000..c8081cd Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png new file mode 100644 index 0000000..b4d7faa Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png new file mode 100644 index 0000000..7dc7f30 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png new file mode 100644 index 0000000..90bc051 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png new file mode 100644 index 0000000..340fd9e Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png new file mode 100644 index 0000000..cf3a14d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png new file mode 100644 index 0000000..cc5e37d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png new file mode 100644 index 0000000..ae2282a Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png new file mode 100644 index 0000000..b97a6ae Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png new file mode 100644 index 0000000..3962cca Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png new file mode 100644 index 0000000..8ccbb27 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png new file mode 100644 index 0000000..367fc8d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png new file mode 100644 index 0000000..56f7b65 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png new file mode 100644 index 0000000..13a7348 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png new file mode 100644 index 0000000..53b4463 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png new file mode 100644 index 0000000..c8081cd Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png new file mode 100644 index 0000000..b4d7faa Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png new file mode 100644 index 0000000..7dc7f30 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png new file mode 100644 index 0000000..90bc051 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png new file mode 100644 index 0000000..340fd9e Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png new file mode 100644 index 0000000..cf3a14d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png new file mode 100644 index 0000000..cc5e37d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png new file mode 100644 index 0000000..ae2282a Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png new file mode 100644 index 0000000..b97a6ae Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png new file mode 100644 index 0000000..3962cca Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png differ diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png new file mode 100644 index 0000000..8ccbb27 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png differ diff --git a/public/icons/windows11/Square44x44Logo.scale-100.png b/public/icons/windows11/Square44x44Logo.scale-100.png new file mode 100644 index 0000000..340fd9e Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-100.png differ diff --git a/public/icons/windows11/Square44x44Logo.scale-125.png b/public/icons/windows11/Square44x44Logo.scale-125.png new file mode 100644 index 0000000..78fbfbd Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-125.png differ diff --git a/public/icons/windows11/Square44x44Logo.scale-150.png b/public/icons/windows11/Square44x44Logo.scale-150.png new file mode 100644 index 0000000..e18f5e6 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-150.png differ diff --git a/public/icons/windows11/Square44x44Logo.scale-200.png b/public/icons/windows11/Square44x44Logo.scale-200.png new file mode 100644 index 0000000..5240c8e Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-200.png differ diff --git a/public/icons/windows11/Square44x44Logo.scale-400.png b/public/icons/windows11/Square44x44Logo.scale-400.png new file mode 100644 index 0000000..42b2456 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-400.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-16.png b/public/icons/windows11/Square44x44Logo.targetsize-16.png new file mode 100644 index 0000000..367fc8d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-16.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-20.png b/public/icons/windows11/Square44x44Logo.targetsize-20.png new file mode 100644 index 0000000..56f7b65 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-20.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-24.png b/public/icons/windows11/Square44x44Logo.targetsize-24.png new file mode 100644 index 0000000..13a7348 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-24.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-256.png b/public/icons/windows11/Square44x44Logo.targetsize-256.png new file mode 100644 index 0000000..53b4463 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-256.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-30.png b/public/icons/windows11/Square44x44Logo.targetsize-30.png new file mode 100644 index 0000000..c8081cd Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-30.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-32.png b/public/icons/windows11/Square44x44Logo.targetsize-32.png new file mode 100644 index 0000000..b4d7faa Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-32.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-36.png b/public/icons/windows11/Square44x44Logo.targetsize-36.png new file mode 100644 index 0000000..7dc7f30 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-36.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-40.png b/public/icons/windows11/Square44x44Logo.targetsize-40.png new file mode 100644 index 0000000..90bc051 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-40.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-44.png b/public/icons/windows11/Square44x44Logo.targetsize-44.png new file mode 100644 index 0000000..340fd9e Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-44.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-48.png b/public/icons/windows11/Square44x44Logo.targetsize-48.png new file mode 100644 index 0000000..cf3a14d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-48.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-60.png b/public/icons/windows11/Square44x44Logo.targetsize-60.png new file mode 100644 index 0000000..cc5e37d Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-60.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-64.png b/public/icons/windows11/Square44x44Logo.targetsize-64.png new file mode 100644 index 0000000..ae2282a Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-64.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-72.png b/public/icons/windows11/Square44x44Logo.targetsize-72.png new file mode 100644 index 0000000..b97a6ae Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-72.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-80.png b/public/icons/windows11/Square44x44Logo.targetsize-80.png new file mode 100644 index 0000000..3962cca Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-80.png differ diff --git a/public/icons/windows11/Square44x44Logo.targetsize-96.png b/public/icons/windows11/Square44x44Logo.targetsize-96.png new file mode 100644 index 0000000..8ccbb27 Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-96.png differ diff --git a/public/icons/windows11/StoreLogo.scale-100.png b/public/icons/windows11/StoreLogo.scale-100.png new file mode 100644 index 0000000..555d6ed Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-100.png differ diff --git a/public/icons/windows11/StoreLogo.scale-125.png b/public/icons/windows11/StoreLogo.scale-125.png new file mode 100644 index 0000000..10895f8 Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-125.png differ diff --git a/public/icons/windows11/StoreLogo.scale-150.png b/public/icons/windows11/StoreLogo.scale-150.png new file mode 100644 index 0000000..e02db47 Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-150.png differ diff --git a/public/icons/windows11/StoreLogo.scale-200.png b/public/icons/windows11/StoreLogo.scale-200.png new file mode 100644 index 0000000..f72f31c Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-200.png differ diff --git a/public/icons/windows11/StoreLogo.scale-400.png b/public/icons/windows11/StoreLogo.scale-400.png new file mode 100644 index 0000000..283f165 Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-400.png differ diff --git a/public/icons/windows11/Wide310x150Logo.scale-100.png b/public/icons/windows11/Wide310x150Logo.scale-100.png new file mode 100644 index 0000000..24f2702 Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-100.png differ diff --git a/public/icons/windows11/Wide310x150Logo.scale-125.png b/public/icons/windows11/Wide310x150Logo.scale-125.png new file mode 100644 index 0000000..67f1006 Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-125.png differ diff --git a/public/icons/windows11/Wide310x150Logo.scale-150.png b/public/icons/windows11/Wide310x150Logo.scale-150.png new file mode 100644 index 0000000..50f6e73 Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-150.png differ diff --git a/public/icons/windows11/Wide310x150Logo.scale-200.png b/public/icons/windows11/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000..5f4626d Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-200.png differ diff --git a/public/icons/windows11/Wide310x150Logo.scale-400.png b/public/icons/windows11/Wide310x150Logo.scale-400.png new file mode 100644 index 0000000..741809a Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-400.png differ diff --git a/public/img/logo/corradAF-logo.svg b/public/img/logo/corradAF-logo.svg new file mode 100644 index 0000000..6242211 --- /dev/null +++ b/public/img/logo/corradAF-logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/img/template/form1.jpg b/public/img/template/form1.jpg new file mode 100644 index 0000000..754419c Binary files /dev/null and b/public/img/template/form1.jpg differ diff --git a/server/api/public/create-notification.post.js b/server/api/public/create-notification.post.js new file mode 100644 index 0000000..22994c7 --- /dev/null +++ b/server/api/public/create-notification.post.js @@ -0,0 +1,348 @@ +import prisma from "~/server/utils/prisma"; +import { processEmailQueue } from "~/server/utils/emailService"; + +const ENV = useRuntimeConfig(); + +// Simple API key auth +function requireApiKey(event) { + const headers = getRequestHeaders(event); + const provided = headers["x-api-key"] || headers["X-API-Key"]; + const expected = ENV.notificationApiKey; // Use private runtime config, not public + + if (!expected) { + throw createError({ + statusCode: 500, + statusMessage: + "Notification API is not configured (missing NUXT_NOTIFICATION_API_KEY environment variable)", + }); + } + if (!provided || provided !== expected) { + throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); + } +} + +// Basic input validation (aligned with internal create API) +function validateInput(body) { + const errors = []; + if (!body?.title?.trim()) errors.push("Title is required"); + if (!body?.type || !["single", "bulk"].includes(body.type)) + errors.push("Type must be either 'single' or 'bulk'"); + if ( + !body?.priority || + !["low", "medium", "high", "critical"].includes(body.priority) + ) + errors.push("Invalid priority"); + if (!body?.category?.trim()) errors.push("Category is required"); + if (!Array.isArray(body?.channels) || body.channels.length === 0) + errors.push("At least one channel is required"); + else { + const validChannels = ["email", "push", "sms"]; + const invalid = body.channels.filter((c) => !validChannels.includes(c)); + if (invalid.length) errors.push(`Invalid channels: ${invalid.join(", ")}`); + } + if ( + !body?.deliveryType || + !["immediate", "scheduled"].includes(body.deliveryType) + ) + errors.push("Invalid deliveryType"); + if ( + !body?.audienceType || + !["all", "specific", "segmented"].includes(body.audienceType) + ) + errors.push("Invalid audienceType"); + if (!body?.contentType || !["new", "template"].includes(body.contentType)) + errors.push("Invalid contentType"); + if (body.deliveryType === "scheduled" && !body.scheduledAt) + errors.push("scheduledAt is required for scheduled delivery"); + if (body.channels?.includes("email") && !body.emailSubject) + errors.push("emailSubject is required when using email channel"); + if (body.contentType === "template" && !body.selectedTemplate) + errors.push("selectedTemplate is required for template content"); + if (body.contentType === "new") { + if (body.channels?.includes("email") && !body.emailContent) + errors.push("emailContent is required for email channel"); + if (body.channels?.includes("push") && (!body.pushTitle || !body.pushBody)) + errors.push("pushTitle and pushBody required for push channel"); + } + if (body.audienceType === "specific") { + if (!Array.isArray(body.specificUsers) || body.specificUsers.length === 0) + errors.push( + "specificUsers must be a non-empty array for specific audience" + ); + } + if ( + body.audienceType === "segmented" && + (!Array.isArray(body.userSegments) || body.userSegments.length === 0) + ) + errors.push("At least one user segment is required"); + return errors; +} + +export default defineEventHandler(async (event) => { + try { + requireApiKey(event); + + const body = await readBody(event); + const validationErrors = validateInput(body); + if (validationErrors.length > 0) { + throw createError({ + statusCode: 400, + statusMessage: "Validation failed", + data: { errors: validationErrors }, + }); + } + + const notificationData = { + title: body.title, + type: body.type, + priority: body.priority, + category: body.category, + channels: body.channels, + emailSubject: body.emailSubject || null, + deliveryType: body.deliveryType, + scheduledAt: body.scheduledAt || null, + timezone: body.timezone || "UTC", + audienceType: body.audienceType, + specificUsers: body.specificUsers || null, + userSegments: body.userSegments || [], + excludeUnsubscribed: body.excludeUnsubscribed !== false, + contentType: body.contentType, + selectedTemplate: body.selectedTemplate || null, + emailContent: body.emailContent || null, + callToActionText: body.callToActionText || null, + callToActionUrl: body.callToActionUrl || null, + pushTitle: body.pushTitle || null, + pushBody: body.pushBody || null, + }; + + const result = await prisma.$transaction(async (tx) => { + // Category + const category = await tx.notification_categories.findFirst({ + where: { value: notificationData.category }, + }); + if (!category) { + throw createError({ + statusCode: 400, + statusMessage: "Invalid category", + }); + } + + // Template (if any) + let templateData = null; + if (notificationData.contentType === "template") { + templateData = await tx.notification_templates.findFirst({ + where: { value: notificationData.selectedTemplate, is_active: true }, + }); + if (!templateData) + throw createError({ + statusCode: 400, + statusMessage: "Invalid or inactive template", + }); + } + + // Estimate audience size (simple heuristic matching internal API) + let estimatedReach = 0; + if (notificationData.audienceType === "all") + estimatedReach = 1; // demo default + else if ( + notificationData.audienceType === "specific" && + notificationData.specificUsers + ) { + estimatedReach = Array.isArray(notificationData.specificUsers) + ? notificationData.specificUsers.length + : notificationData.specificUsers.split("\n").filter((l) => l.trim()).length; + } else if (notificationData.audienceType === "segmented") { + estimatedReach = notificationData.userSegments?.length || 0; + } + + // Create notification + const notification = await tx.notifications.create({ + data: { + title: notificationData.title, + type: notificationData.type, + priority: notificationData.priority, + category_id: category.id, + delivery_type: notificationData.deliveryType, + scheduled_at: notificationData.scheduledAt + ? new Date(notificationData.scheduledAt) + : null, + timezone: notificationData.timezone, + enable_tracking: true, + audience_type: notificationData.audienceType, + specific_users: Array.isArray(notificationData.specificUsers) + ? notificationData.specificUsers.join("\n") + : notificationData.specificUsers, + exclude_unsubscribed: notificationData.excludeUnsubscribed, + respect_do_not_disturb: true, + content_type: notificationData.contentType, + template_id: templateData?.id || null, + email_subject: + notificationData.emailSubject || templateData?.subject || null, + email_content: + notificationData.emailContent || + templateData?.email_content || + null, + call_to_action_text: notificationData.callToActionText || null, + call_to_action_url: notificationData.callToActionUrl || null, + push_title: + notificationData.pushTitle || templateData?.push_title || null, + push_body: + notificationData.pushBody || templateData?.push_body || null, + estimated_reach: estimatedReach, + created_by: "public-api", + status: + notificationData.deliveryType === "immediate" + ? "sending" + : "scheduled", + }, + }); + + // Channels + await tx.notification_channels.createMany({ + data: notificationData.channels.map((c) => ({ + notification_id: notification.id, + channel_type: c, + })), + }); + + // Segments + if ( + notificationData.audienceType === "segmented" && + notificationData.userSegments?.length > 0 + ) { + for (const seg of notificationData.userSegments) { + const segRow = await tx.user_segments.findFirst({ + where: { value: seg, is_active: true }, + }); + if (segRow) { + await tx.notification_user_segments.create({ + data: { notification_id: notification.id, segment_id: segRow.id }, + }); + } + } + } + + // Recipients list (demo-friendly) + const recipients = []; + if (notificationData.audienceType === "all") { + const senderEmail = process.env.SMTP_USER || "test@example.com"; + recipients.push({ user_id: "public-user-1", email: senderEmail }); + } else if ( + notificationData.audienceType === "specific" && + notificationData.specificUsers + ) { + const ids = Array.isArray(notificationData.specificUsers) + ? notificationData.specificUsers + : notificationData.specificUsers.split("\n").map((l) => l.trim()).filter(Boolean); + for (const id of ids) { + const isEmail = id.includes("@"); + recipients.push({ + user_id: isEmail ? id.split("@")[0] : id, + email: isEmail ? id : `${id}@example.com`, + }); + } + } else if (notificationData.audienceType === "segmented") { + for (let i = 0; i < (notificationData.userSegments?.length || 0); i++) { + const segVal = notificationData.userSegments[i]; + recipients.push({ + user_id: `${segVal}-user-${i + 1}`, + email: `${segVal}${i + 1}@example.com`, + }); + } + } + + // Create recipients + queue using batch inserts for better performance + console.log(`📦 Processing ${recipients.length} recipients across ${notificationData.channels.length} channel(s)...`); + + // Prepare batch data for recipients + const recipientsData = []; + for (const r of recipients) { + for (const ch of notificationData.channels) { + recipientsData.push({ + notification_id: notification.id, + user_id: r.user_id, + email: ch === "email" ? r.email : null, + channel_type: ch, + status: "pending", + }); + } + } + + // Batch insert all recipients at once + await tx.notification_recipients.createMany({ + data: recipientsData, + }); + + // Fetch created recipients to get their IDs for queue + const createdRecipients = await tx.notification_recipients.findMany({ + where: { notification_id: notification.id }, + orderBy: { id: 'asc' } + }); + + // Prepare batch data for queue + let scheduledFor; + if (notificationData.deliveryType === "immediate") + scheduledFor = new Date(Date.now() + 60000); + else if (notificationData.scheduledAt) + scheduledFor = new Date(notificationData.scheduledAt); + else scheduledFor = new Date(Date.now() + 300000); + + const priority = notificationData.priority === "critical" ? 1 + : notificationData.priority === "high" ? 2 + : notificationData.priority === "medium" ? 3 + : 5; + + const queueData = createdRecipients.map(rec => ({ + notification_id: notification.id, + recipient_id: rec.id, + scheduled_for: scheduledFor, + priority, + status: "queued", + })); + + // Batch insert all queue items at once + await tx.notification_queue.createMany({ + data: queueData, + }); + + console.log(`✅ Created ${createdRecipients.length} recipients and queue items in batch`); + + return { + id: notification.id, + estimatedReach, + recipientsCreated: createdRecipients.length, + }; + }); + + // Process queue in background (non-blocking) + if (body.deliveryType === "immediate") { + // Don't await - let it run in background + processEmailQueue().catch(err => { + console.error('Background queue processing error:', err); + }); + } + + return { + success: true, + data: { + id: result.id, + message: + body.deliveryType === "immediate" + ? "Notification queued for immediate delivery" + : "Notification has been scheduled", + estimatedReach: result.estimatedReach, + recipientsQueued: result.recipientsCreated || 0, + }, + }; + } catch (error) { + console.log("Error:", error); + + if (error.statusCode) throw error; + throw createError({ + statusCode: 500, + statusMessage: "Failed to create notification", + data: { error: error.message }, + }); + } finally { + } +}); diff --git a/server/api/public/get-notification-list.get.js b/server/api/public/get-notification-list.get.js new file mode 100644 index 0000000..6256ca2 --- /dev/null +++ b/server/api/public/get-notification-list.get.js @@ -0,0 +1,131 @@ +import { z } from "zod"; +import prisma from "~/server/utils/prisma"; + +const ENV = useRuntimeConfig(); + +function requireApiKey(event) { + const headers = getRequestHeaders(event); + const provided = headers["x-api-key"] || headers["X-API-Key"]; + const expected = ENV.notificationApiKey; + if (!expected) { + throw createError({ + statusCode: 500, + statusMessage: + "Notification API is not configured (missing NUXT_NOTIFICATION_API_KEY environment variable)", + }); + } + if (!provided || provided !== expected) { + throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); + } +} + +const listSchema = z.object({ + page: z + .string() + .optional() + .transform((v) => (v ? parseInt(v) : 1)) + .default("1"), + limit: z + .string() + .optional() + .transform((v) => (v ? parseInt(v) : 20)) + .default("20"), + status: z.string().optional(), + priority: z.string().optional(), + category: z.string().optional(), + search: z.string().optional(), + sortBy: z.string().default("created_at"), + sortOrder: z.enum(["asc", "desc"]).default("desc"), +}); + +export default defineEventHandler(async (event) => { + try { + requireApiKey(event); + const params = listSchema.parse(getQuery(event) || {}); + + const where = {}; + if (params.status) where.status = params.status; + if (params.priority) where.priority = params.priority; + if (params.category) + where.notification_categories = { value: params.category }; + if (params.search) { + where.OR = [ + { title: { contains: params.search } }, + { email_subject: { contains: params.search } }, + { push_title: { contains: params.search } }, + ]; + } + + const total = await prisma.notifications.count({ where }); + + const notifications = await prisma.notifications.findMany({ + where, + select: { + id: true, + title: true, + priority: true, + status: true, + delivery_type: true, + scheduled_at: true, + created_at: true, + notification_categories: { select: { name: true } }, + notification_channels: { select: { channel_type: true } }, + notification_recipients: { select: { status: true } }, + }, + orderBy: { [params.sortBy]: params.sortOrder }, + skip: (params.page - 1) * params.limit, + take: params.limit, + }); + + const items = notifications.map((n) => { + const totalRecipients = n.notification_recipients.length; + const delivered = n.notification_recipients.filter( + (r) => r.status === "delivered" + ).length; + const successRate = + totalRecipients > 0 + ? Math.round((delivered / totalRecipients) * 100) + : 0; + return { + id: n.id, + title: n.title, + category: n.notification_categories?.name || "Uncategorized", + channels: n.notification_channels.map((c) => c.channel_type), + priority: n.priority, + status: n.status, + recipients: totalRecipients, + successRate, + createdAt: n.created_at, + }; + }); + + const totalPages = Math.ceil(total / params.limit); + + return { + success: true, + data: { + notifications: items, + pagination: { + page: params.page, + totalPages, + totalItems: total, + hasMore: params.page < totalPages, + }, + }, + }; + } catch (error) { + if (error instanceof z.ZodError) { + throw createError({ + statusCode: 400, + statusMessage: "Invalid query parameters", + data: error.errors, + }); + } + if (error.statusCode) throw error; + throw createError({ + statusCode: 500, + statusMessage: "Failed to fetch notifications", + data: { error: error.message }, + }); + } +});