Refactor database layer: convert to DatabaseProvider class with initialization

This commit is contained in:
dmit.b
2026-05-08 12:15:56 +03:00
commit 49bb854ca2
28 changed files with 2011 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
.dockerignore
Dockerfile
build/
.dart_tool/
.git/
.github/
.gitignore
.idea/
.packages
+9
View File
@@ -0,0 +1,9 @@
# Environment variables for sky_kfm_backend
# JWT secret used for signing tokens
JWT_SECRET=your-super-secret-key-change-me
# Secret pepper added to passwords before hashing (should be random and kept secret)
PASSWORD_PEPPER=your-random-pepper-string-change-me
# Database connection URL
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/family_safety
# TOKEN_LIFETIME in minutes
TOKEN_LIFETIME=600
+3
View File
@@ -0,0 +1,3 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+13
View File
@@ -0,0 +1,13 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyInterpreterInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N802" />
</list>
</option>
</inspection_tool>
</profile>
</component>
+524
View File
@@ -0,0 +1,524 @@
<component name="libraryTable">
<library name="Dart Packages" type="DartPackagesLibraryType">
<properties>
<option name="packageNameToDirsMap">
<entry key="_fe_analyzer_shared">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/_fe_analyzer_shared-99.0.0/lib" />
</list>
</value>
</entry>
<entry key="adaptive_number">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/adaptive_number-1.0.0/lib" />
</list>
</value>
</entry>
<entry key="analyzer">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/analyzer-12.1.0/lib" />
</list>
</value>
</entry>
<entry key="args">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/args-2.7.0/lib" />
</list>
</value>
</entry>
<entry key="async">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/async-2.13.1/lib" />
</list>
</value>
</entry>
<entry key="bcrypt">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/bcrypt-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/boolean_selector-2.1.2/lib" />
</list>
</value>
</entry>
<entry key="buffer">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/buffer-1.2.3/lib" />
</list>
</value>
</entry>
<entry key="charcode">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/charcode-1.4.0/lib" />
</list>
</value>
</entry>
<entry key="cli_config">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/cli_config-0.2.0/lib" />
</list>
</value>
</entry>
<entry key="clock">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/clock-1.1.2/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/collection-1.19.1/lib" />
</list>
</value>
</entry>
<entry key="convert">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/convert-3.1.2/lib" />
</list>
</value>
</entry>
<entry key="coverage">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/coverage-1.15.0/lib" />
</list>
</value>
</entry>
<entry key="crypto">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/crypto-3.0.7/lib" />
</list>
</value>
</entry>
<entry key="dart_jsonwebtoken">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dart_jsonwebtoken-2.17.0/lib" />
</list>
</value>
</entry>
<entry key="dotenv">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dotenv-4.2.0/lib" />
</list>
</value>
</entry>
<entry key="ed25519_edwards">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/ed25519_edwards-0.3.1/lib" />
</list>
</value>
</entry>
<entry key="file">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/file-7.0.1/lib" />
</list>
</value>
</entry>
<entry key="fixnum">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/fixnum-1.1.1/lib" />
</list>
</value>
</entry>
<entry key="frontend_server_client">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/frontend_server_client-4.0.0/lib" />
</list>
</value>
</entry>
<entry key="glob">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/glob-2.1.3/lib" />
</list>
</value>
</entry>
<entry key="http">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http-1.6.0/lib" />
</list>
</value>
</entry>
<entry key="http_methods">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_methods-1.1.1/lib" />
</list>
</value>
</entry>
<entry key="http_multi_server">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_multi_server-3.2.2/lib" />
</list>
</value>
</entry>
<entry key="http_parser">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_parser-4.1.2/lib" />
</list>
</value>
</entry>
<entry key="io">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/io-1.0.5/lib" />
</list>
</value>
</entry>
<entry key="js">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/js-0.7.2/lib" />
</list>
</value>
</entry>
<entry key="lints">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/lints-6.1.0/lib" />
</list>
</value>
</entry>
<entry key="logging">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/logging-1.3.0/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/matcher-0.12.20/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.18.2/lib" />
</list>
</value>
</entry>
<entry key="mime">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/mime-2.0.0/lib" />
</list>
</value>
</entry>
<entry key="node_preamble">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/node_preamble-2.0.2/lib" />
</list>
</value>
</entry>
<entry key="package_config">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/package_config-2.2.0/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path-1.9.1/lib" />
</list>
</value>
</entry>
<entry key="pointycastle">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pointycastle-3.9.1/lib" />
</list>
</value>
</entry>
<entry key="pool">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pool-1.5.2/lib" />
</list>
</value>
</entry>
<entry key="postgres">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/postgres-3.5.10/lib" />
</list>
</value>
</entry>
<entry key="pub_semver">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pub_semver-2.2.0/lib" />
</list>
</value>
</entry>
<entry key="shelf">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf-1.4.2/lib" />
</list>
</value>
</entry>
<entry key="shelf_packages_handler">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_packages_handler-3.0.2/lib" />
</list>
</value>
</entry>
<entry key="shelf_router">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_router-1.1.4/lib" />
</list>
</value>
</entry>
<entry key="shelf_static">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_static-1.1.3/lib" />
</list>
</value>
</entry>
<entry key="shelf_web_socket">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_web_socket-3.0.0/lib" />
</list>
</value>
</entry>
<entry key="source_map_stack_trace">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_map_stack_trace-2.1.2/lib" />
</list>
</value>
</entry>
<entry key="source_maps">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_maps-0.10.13/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_span-1.10.2/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stack_trace-1.12.1/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stream_channel-2.1.4/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/string_scanner-1.4.1/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/term_glyph-1.2.2/lib" />
</list>
</value>
</entry>
<entry key="test">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test-1.31.1/lib" />
</list>
</value>
</entry>
<entry key="test_api">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test_api-0.7.12/lib" />
</list>
</value>
</entry>
<entry key="test_core">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test_core-0.6.18/lib" />
</list>
</value>
</entry>
<entry key="typed_data">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/typed_data-1.4.0/lib" />
</list>
</value>
</entry>
<entry key="uuid">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/uuid-4.5.3/lib" />
</list>
</value>
</entry>
<entry key="vm_service">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vm_service-15.2.0/lib" />
</list>
</value>
</entry>
<entry key="watcher">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/watcher-1.2.1/lib" />
</list>
</value>
</entry>
<entry key="web">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web-1.1.1/lib" />
</list>
</value>
</entry>
<entry key="web_socket">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web_socket-1.0.1/lib" />
</list>
</value>
</entry>
<entry key="web_socket_channel">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web_socket_channel-3.0.3/lib" />
</list>
</value>
</entry>
<entry key="webkit_inspection_protocol">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/lib" />
</list>
</value>
</entry>
<entry key="yaml">
<value>
<list>
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/yaml-3.1.3/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/_fe_analyzer_shared-99.0.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/adaptive_number-1.0.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/analyzer-12.1.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/args-2.7.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/async-2.13.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/bcrypt-1.2.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/boolean_selector-2.1.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/buffer-1.2.3/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/charcode-1.4.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/cli_config-0.2.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/clock-1.1.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/collection-1.19.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/convert-3.1.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/coverage-1.15.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/crypto-3.0.7/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dart_jsonwebtoken-2.17.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dotenv-4.2.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/ed25519_edwards-0.3.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/file-7.0.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/fixnum-1.1.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/frontend_server_client-4.0.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/glob-2.1.3/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http-1.6.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_methods-1.1.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_multi_server-3.2.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_parser-4.1.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/io-1.0.5/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/js-0.7.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/lints-6.1.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/logging-1.3.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/matcher-0.12.20/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.18.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/mime-2.0.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/node_preamble-2.0.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/package_config-2.2.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path-1.9.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pointycastle-3.9.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pool-1.5.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/postgres-3.5.10/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pub_semver-2.2.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf-1.4.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_packages_handler-3.0.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_router-1.1.4/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_static-1.1.3/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_web_socket-3.0.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_map_stack_trace-2.1.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_maps-0.10.13/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_span-1.10.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stack_trace-1.12.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stream_channel-2.1.4/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/string_scanner-1.4.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/term_glyph-1.2.2/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test-1.31.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test_api-0.7.12/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test_core-0.6.18/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/typed_data-1.4.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/uuid-4.5.3/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vm_service-15.2.0/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/watcher-1.2.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web-1.1.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web_socket-1.0.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web_socket_channel-3.0.3/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/lib" />
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/yaml-3.1.3/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+31
View File
@@ -0,0 +1,31 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/_internal" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/async" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/cli" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/collection" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/concurrent" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/convert" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/core" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/developer" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/ffi" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/html" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/io" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/isolate" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/js" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/js_interop" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/js_interop_unsafe" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/js_util" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/math" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/svg" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://$PROJECT_DIR$/../../../../tools/flutter/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/family_safety_tracker.iml" filepath="$PROJECT_DIR$/family_safety_tracker.iml" />
</modules>
</component>
</project>
+62
View File
@@ -0,0 +1,62 @@
# AGENTS.md - Family Safety Tracker
## Overview
Dart server app (Shelf + PostgreSQL) for sharing geolocation with family members. REST API with JWT auth, bcrypt password hashing, and auto-expiring position data.
## Quick Start
```bash
dart pub get
dart run bin/server.dart
```
Server starts on port `8080` (override with `PORT` env var).
## Code Structure
- **`bin/`** - All application code lives here (not `lib/`, which is empty).
- `bin/server.dart` - Entry point. Starts Shelf server, database connection, and cleanup timer.
- `bin/routes/` - Route handlers (`auth_routes.dart`, `user_routes.dart`, `geo_routes.dart`)
- `bin/repositories/` - Data access layer (`user_repo.dart`, `geoposition_repo.dart`)
- `bin/models/` - Domain models (`user.dart`, `geoposition.dart`, `log.dart`)
- `bin/database/` - Database connection and auto-migration
- **`lib/`** - Empty scaffold directory (stale, can be ignored).
## Database
- PostgreSQL with connection via environment variables (`POSTGRES_HOST`, `POSTGRES_DB`, etc.) or defaults.
- **Auto-migration**: Tables are created on startup via `Database.initialize()`. No manual migration needed.
- Tables: `users`, `geopositions`, `logs`.
## Runtime Behavior
- Expired geopositions are cleaned up every 5 minutes by a periodic timer in `server.dart`.
- Share links use UUIDs stored in-memory via `GeopositionRepository`.
## API Endpoints
- `POST /login` - Authenticate with login/password, returns user data.
- `/user` - CRUD for users.
- `/geo` - POST to create position, UPDATE to update (with lifetime expiry).
- `GET /watch?unique_id=...` - Returns latest position for a share link.
- `/share` - Creates a one-time share link UUID.
## Testing
```bash
dart test
```
Tests spawn the server process and hit endpoints. Server listens on port `8080` by default.
## Build/Quality Commands
```bash
dart analyze # Static analysis (uses analysis_options.yaml)
dart format --set . # Format code
```
## Dependencies
- `shelf`, `shelf_router` - HTTP framework
- `postgres` - PostgreSQL driver
- `bcrypt` - Password hashing
- `dart_jsonwebtoken` - JWT tokens
- `dotenv` - Environment config
- `uuid` - UUID generation
## Notes
- SDK: `^3.10.1`
- No Docker setup present (`.dockerignore` exists but no `Dockerfile`).
- Documentation in `README.md` is in Russian; the API is designed for geolocation sharing between family members.
- `test_bcrypt.dart` is a standalone test file, not part of the test suite.
+3
View File
@@ -0,0 +1,3 @@
## 1.0.0
- Initial version.
+18
View File
@@ -0,0 +1,18 @@
A server app built using [Shelf](https://pub.dev/packages/shelf),
Это новый проект.
Давай добавим описание проекта -
Сервер - приложения для реализации возможности делиться геопозицией с другим человеком.
База данных (в данном случае это будет postges) с отметками о геопозиции человека
Какие таблицы будут в базе данных -
Пользователи - ID, login, pwd
Geoposition - id, x value, y value, datetime, lifetime
Logs - username, action, datetime
Основа приложения - это REST API.
Вот какие методы нужны.
/login - авторизация.
/user - CRUD пользователей.
/geo - POST - создание позиции, UPDATE - обновление позиции. (при создании указывается время жизни, после которого данные будут удалены из базы)
/watch?{unique id} - возращает геопозицию+время последней отметки + оставшееся время жизни отметки
/share - метод который создает одноразовую ссылку (/watch?{unique id} ) - по которой доступны данные о геопозиции. В памяти приложения создается связь между geo из таблицы и {unique id}
+30
View File
@@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options
+356
View File
@@ -0,0 +1,356 @@
import 'dart:io';
import 'package:bcrypt/bcrypt.dart';
import 'package:dotenv/dotenv.dart';
import 'package:postgres/postgres.dart';
import 'package:uuid/uuid.dart';
import '../models/user.dart';
import '../models/geoposition.dart';
import '../models/log.dart';
class DatabaseProvider {
late Connection _dbConnection;
final Map<String, int> _shareLinks = {};
final _uuid = const Uuid();
Future<void> initialize() async {
final dotenv = DotEnv();
final host = dotenv['POSTGRES_HOST'] ?? 'localhost';
final port = int.parse(dotenv['POSTGRES_PORT'] ?? '5432');
final databaseName = dotenv['POSTGRES_DB'] ?? 'family_safety';
final username = dotenv['POSTGRES_USER'] ?? 'postgres';
final password = dotenv['POSTGRES_PASSWORD'] ?? '';
try {
final defaultConnection = await Connection.open(
settings: ConnectionSettings(sslMode: SslMode.disable),
Endpoint(
host: host,
port: port,
database: 'postgres',
username: username,
password: password,
),
);
final results = await defaultConnection.execute(
Sql.named('SELECT 1 FROM pg_database WHERE datname = @dbName'),
parameters: {'dbName': databaseName},
);
if (results.isEmpty) {
await defaultConnection.execute(
Sql.named('CREATE DATABASE @dbName'),
parameters: {'dbName': databaseName},
);
print('Database $databaseName created.');
} else {
print('Database $databaseName already exists.');
}
await defaultConnection.close();
_dbConnection = await Connection.open(
settings: ConnectionSettings(sslMode: SslMode.disable),
Endpoint(
host: host,
port: port,
database: databaseName,
username: username,
password: password,
),
);
print('Connected to database $databaseName.');
await _dbConnection.execute(
Sql.named('''
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
login VARCHAR(255) UNIQUE NOT NULL,
pwd_hash VARCHAR(255) NOT NULL
)
'''),
);
await _dbConnection.execute(
Sql.named('''
CREATE TABLE IF NOT EXISTS geopositions (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
x_value DOUBLE PRECISION NOT NULL,
y_value DOUBLE PRECISION NOT NULL,
datetime TIMESTAMP NOT NULL DEFAULT NOW(),
lifetime INTERVAL NOT NULL,
expires_at TIMESTAMP NOT NULL
)
'''),
);
await _dbConnection.execute(
Sql.named('''
CREATE TABLE IF NOT EXISTS logs (
id SERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL,
action VARCHAR(255) NOT NULL,
datetime TIMESTAMP NOT NULL DEFAULT NOW()
)
'''),
);
print('All tables ensured to exist.');
} catch (e, stackTrace) {
stderr.writeln('Database initialization error: $e');
stderr.writeln(stackTrace);
exit(1);
}
}
Future<void> close() async {
await _dbConnection.close();
}
// ==================== User operations ====================
Future<User?> findUserByLogin(String login) async {
final results = await _dbConnection.execute(
Sql.named('SELECT id, login, pwd_hash FROM users WHERE login = @login'),
parameters: {'login': login},
);
if (results.isEmpty) return null;
final row = results.first;
return User(
id: int.parse(row[0].toString()),
login: row[1] as String,
pwdHash: row[2] as String,
);
}
Future<User> createUser(String login, String password) async {
final hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
final results = await _dbConnection.execute(
Sql.named('''
INSERT INTO users (login, pwd_hash) VALUES (@login, @pwdHash)
RETURNING id, login, pwd_hash
'''),
parameters: {
'login': login,
'pwdHash': hashedPassword,
},
);
final row = results.first;
return User(
id: int.parse(row[0].toString()),
login: row[1] as String,
pwdHash: row[2] as String,
);
}
Future<void> deleteUser(int id) async {
await _dbConnection.execute(
Sql.named('DELETE FROM users WHERE id = @id'),
parameters: {'id': id},
);
}
Future<User> updateUser(int id, String newLogin, String? newPassword) async {
if (newPassword != null) {
final hashedPassword = BCrypt.hashpw(newPassword, BCrypt.gensalt());
await _dbConnection.execute(
Sql.named('''
UPDATE users SET login = @login, pwd_hash = @pwdHash WHERE id = @id
'''),
parameters: {
'login': newLogin,
'pwdHash': hashedPassword,
'id': id,
},
);
} else {
await _dbConnection.execute(
Sql.named('UPDATE users SET login = @login WHERE id = @id'),
parameters: {
'login': newLogin,
'id': id,
},
);
}
final user = await findUserByLogin(newLogin);
return user!;
}
Future<List<User>> getAllUsers() async {
final results = await _dbConnection.execute(
Sql.named('SELECT id, login, pwd_hash FROM users'),
);
return results
.map(
(row) => User(
id: int.parse(row[0].toString()),
login: row[1] as String,
pwdHash: row[2] as String,
),
)
.toList();
}
// ==================== Geoposition operations ====================
Future<Geoposition> createPosition(
int userId,
double x,
double y,
Duration lifetime,
) async {
final expiresAt = DateTime.now().add(lifetime);
final results = await _dbConnection.execute(
Sql.named('''
INSERT INTO geopositions (user_id, x_value, y_value, datetime, lifetime, expires_at)
VALUES (@userId, @xValue, @yValue, NOW(), @lifetime, @expiresAt)
RETURNING id, user_id, x_value, y_value, datetime, lifetime, expires_at
'''),
parameters: {
'userId': userId,
'xValue': x,
'yValue': y,
'lifetime': _toInterval(lifetime),
'expiresAt': expiresAt.toIso8601String(),
},
);
final row = results.first;
return Geoposition(
id: int.parse(row[0].toString()),
userId: int.parse(row[1].toString()),
xValue: double.parse(row[2].toString()),
yValue: double.parse(row[3].toString()),
datetime: DateTime.parse(row[4].toString()),
lifetime: lifetime,
expiresAt: DateTime.parse(row[6].toString()),
);
}
Future<Geoposition> updatePosition(
int userId,
double x,
double y,
Duration lifetime,
) async {
final expiresAt = DateTime.now().add(lifetime);
final results = await _dbConnection.execute(
Sql.named('''
INSERT INTO geopositions (user_id, x_value, y_value, datetime, lifetime, expires_at)
VALUES (@userId, @xValue, @yValue, NOW(), @lifetime, @expiresAt)
RETURNING id, user_id, x_value, y_value, datetime, lifetime, expires_at
'''),
parameters: {
'userId': userId,
'xValue': x,
'yValue': y,
'lifetime': _toInterval(lifetime),
'expiresAt': expiresAt.toIso8601String(),
},
);
final row = results.first;
return Geoposition(
id: int.parse(row[0].toString()),
userId: int.parse(row[1].toString()),
xValue: double.parse(row[2].toString()),
yValue: double.parse(row[3].toString()),
datetime: DateTime.parse(row[4].toString()),
lifetime: lifetime,
expiresAt: DateTime.parse(row[6].toString()),
);
}
Future<Geoposition?> getLatestPosition(int userId) async {
final results = await _dbConnection.execute(
Sql.named('''
SELECT id, user_id, x_value, y_value, datetime, lifetime, expires_at
FROM geopositions
WHERE user_id = @userId AND expires_at > NOW()
ORDER BY datetime DESC
LIMIT 1
'''),
parameters: {'userId': userId},
);
if (results.isEmpty) return null;
final row = results.first;
return Geoposition(
id: int.parse(row[0].toString()),
userId: int.parse(row[1].toString()),
xValue: double.parse(row[2].toString()),
yValue: double.parse(row[3].toString()),
datetime: DateTime.parse(row[4].toString()),
lifetime: Duration(
seconds: int.tryParse(row[5].toString()) ?? 0),
expiresAt: DateTime.parse(row[6].toString()),
);
}
Future<void> cleanupExpired() async {
await _dbConnection.execute(
Sql.named('DELETE FROM geopositions WHERE expires_at < NOW()'),
);
}
// ==================== Share operations ====================
String createShareId(int userId) {
final uniqueId = _uuid.v4();
_shareLinks[uniqueId] = userId;
return uniqueId;
}
int? getUserIdByShareId(String uniqueId) {
return _shareLinks[uniqueId];
}
// ==================== Log operations ====================
Future<void> createLog(String username, String action) async {
await _dbConnection.execute(
Sql.named('''
INSERT INTO logs (username, action) VALUES (@username, @action)
'''),
parameters: {
'username': username,
'action': action,
},
);
}
Future<List<Log>> getAllLogs() async {
final results = await _dbConnection.execute(
Sql.named('SELECT id, username, action, datetime FROM logs'),
);
return results
.map(
(row) => Log(
id: int.parse(row[0].toString()),
username: row[1] as String,
action: row[2] as String,
datetime: DateTime.parse(row[3].toString()),
),
)
.toList();
}
String _toInterval(Duration duration) {
final seconds = duration.inSeconds;
return '$seconds seconds';
}
}
+22
View File
@@ -0,0 +1,22 @@
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
login VARCHAR(255) UNIQUE NOT NULL,
pwd_hash VARCHAR(255) NOT NULL
);
CREATE TABLE IF NOT EXISTS geopositions (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
x_value DOUBLE PRECISION NOT NULL,
y_value DOUBLE PRECISION NOT NULL,
datetime TIMESTAMP NOT NULL DEFAULT NOW(),
lifetime INTERVAL NOT NULL,
expires_at TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS logs (
id SERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL,
action VARCHAR(255) NOT NULL,
datetime TIMESTAMP NOT NULL DEFAULT NOW()
);
+41
View File
@@ -0,0 +1,41 @@
class Geoposition {
final int id;
final int userId;
final double xValue;
final double yValue;
final DateTime datetime;
final Duration lifetime;
final DateTime expiresAt;
Geoposition({
required this.id,
required this.userId,
required this.xValue,
required this.yValue,
required this.datetime,
required this.lifetime,
required this.expiresAt,
});
Map<String, dynamic> toJson() {
return {
'id': id,
'x': xValue,
'y': yValue,
'datetime': datetime.toIso8601String(),
'remainingSeconds': expiresAt.difference(DateTime.now()).inSeconds,
};
}
factory Geoposition.fromMap(Map<String, dynamic> map) {
return Geoposition(
id: map['id'],
userId: map['user_id'],
xValue: map['x_value'].toDouble(),
yValue: map['y_value'].toDouble(),
datetime: map['datetime'] as DateTime,
lifetime: Duration(seconds: map['lifetime']),
expiresAt: map['expires_at'] as DateTime,
);
}
}
+22
View File
@@ -0,0 +1,22 @@
class Log {
final int id;
final String username;
final String action;
final DateTime datetime;
Log({
required this.id,
required this.username,
required this.action,
required this.datetime,
});
factory Log.fromMap(Map<String, dynamic> map) {
return Log(
id: map['id'],
username: map['username'],
action: map['action'],
datetime: map['datetime'] as DateTime,
);
}
}
+26
View File
@@ -0,0 +1,26 @@
class User {
final int id;
final String login;
final String pwdHash;
User({
required this.id,
required this.login,
required this.pwdHash,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'login': login,
};
}
factory User.fromMap(Map<String, dynamic> map) {
return User(
id: map['id'],
login: map['login'],
pwdHash: map['pwd_hash'],
);
}
}
+52
View File
@@ -0,0 +1,52 @@
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
import 'package:bcrypt/bcrypt.dart';
import '../database/database_provider.dart';
import 'dart:convert';
class AuthRoutes {
final DatabaseProvider database;
AuthRoutes(this.database);
Router get routes {
final router = Router();
router.post('/login', _login);
router.get('/watch', _watch);
return router;
}
Future<Response> _login(Request request) async {
final body = await request.readAsString();
final data = jsonDecode(body);
final login = data['login'];
final password = data['password'];
final user = await database.findUserByLogin(login);
if (user == null || !BCrypt.checkpw(password, user.pwdHash)) {
return Response(401, body: 'Invalid credentials');
}
return Response(200, body: jsonEncode({'user': user.toMap()}));
}
Future<Response> _watch(Request request) async {
final uniqueId = request.url.queryParameters['unique_id'];
final userId = database.getUserIdByShareId(uniqueId!);
if (userId == null) {
return Response(404, body: 'Share link not found');
}
final position = await database.getLatestPosition(userId);
if (position == null) {
return Response(404, body: 'No position available');
}
return Response(200, body: position.toJson());
}
}
+56
View File
@@ -0,0 +1,56 @@
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
import '../database/database_provider.dart';
import 'dart:convert';
class GeoRoutes {
final DatabaseProvider database;
GeoRoutes(this.database);
Router get routes {
final router = Router();
router.post('/geo', _createPosition);
router.put('/geo', _updatePosition);
router.post('/share', _createShare);
return router;
}
Future<Response> _createPosition(Request request) async {
final body = await request.readAsString();
final data = jsonDecode(body);
final userId = data['user_id'];
final x = data['x'];
final y = data['y'];
final lifetimeSeconds = data['lifetime'];
final lifetime = Duration(seconds: lifetimeSeconds);
final position = await database.createPosition(userId, x, y, lifetime);
return Response(201, body: position.toJson());
}
Future<Response> _updatePosition(Request request) async {
final body = await request.readAsString();
final data = jsonDecode(body);
final userId = data['user_id'];
final x = data['x'];
final y = data['y'];
final lifetimeSeconds = data['lifetime'];
final lifetime = Duration(seconds: lifetimeSeconds);
final position = await database.updatePosition(userId, x, y, lifetime);
return Response(200, body: position.toJson());
}
Future<Response> _createShare(Request request) async {
final body = await request.readAsString();
final data = jsonDecode(body);
final userId = data['user_id'];
final shareId = database.createShareId(userId);
return Response(200, body: jsonEncode({'share_id': shareId}));
}
}
+54
View File
@@ -0,0 +1,54 @@
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
import '../database/database_provider.dart';
import 'dart:convert';
class UserRoutes {
final DatabaseProvider database;
UserRoutes(this.database);
Router get routes {
final router = Router();
router.get('/user', _getAllUsers);
router.post('/user', _createUser);
router.put('/user/<id>', _updateUser);
router.delete('/user/<id>', _deleteUser);
return router;
}
Future<Response> _getAllUsers(Request request) async {
final users = await database.getAllUsers();
return Response(200, body: jsonEncode(users.map((u) => u.toMap()).toList()));
}
Future<Response> _createUser(Request request) async {
final body = await request.readAsString();
final data = jsonDecode(body);
final login = data['login'];
final password = data['password'];
final user = await database.createUser(login, password);
return Response(201, body: jsonEncode(user.toMap()));
}
Future<Response> _updateUser(Request request) async {
final id = int.parse(request.params['id']!);
final body = await request.readAsString();
final data = jsonDecode(body);
final login = data['login'];
final password = data['password'];
final user = await database.updateUser(id, login, password);
return Response(200, body: jsonEncode(user.toMap()));
}
Future<Response> _deleteUser(Request request) async {
final id = int.parse(request.params['id']!);
await database.deleteUser(id);
return Response(204);
}
}
+39
View File
@@ -0,0 +1,39 @@
import 'dart:io';
import 'dart:async';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_router/shelf_router.dart';
import 'database/database_provider.dart';
import 'routes/auth_routes.dart';
import 'routes/user_routes.dart';
import 'routes/geo_routes.dart';
void main(List<String> args) async {
final database = DatabaseProvider();
await database.initialize();
Timer.periodic(const Duration(minutes: 5), (timer) {
database.cleanupExpired();
});
final authRoutes = AuthRoutes(database);
final userRoutes = UserRoutes(database);
final geoRoutes = GeoRoutes(database);
final router = Router()
..mount('', authRoutes.routes.call)
..mount('', userRoutes.routes.call)
..mount('', geoRoutes.routes.call)
..get('/', (Request req) => Response.ok('Family Safety Tracker API\n'));
final handler = Pipeline()
.addMiddleware(logRequests())
.addHandler(router.call);
final ip = InternetAddress.anyIPv4;
final port = int.parse(Platform.environment['PORT'] ?? '8080');
final server = await serve(handler, ip, port);
print('Server listening on port ${server.port}');
}
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
+28
View File
@@ -0,0 +1,28 @@
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"llama.cpp": {
"npm": "@ai-sdk/openai-compatible",
"name": "llama-server (local)",
"options": {
"baseURL": "http://127.0.0.1:9988/v1"
},
"models": {
"qwen3-coder:a3b": {
"name": "Qwen_Qwen3.5-9B-Q6_K (local)",
"limit": {
"context": 42000,
"output": 42000
}
}
}
}
},
"mcp": {
"IntelliJIdea": {
"type": "remote",
"url": "http://127.0.0.1:64342/stream",
"enabled": true
}
}
}
+517
View File
@@ -0,0 +1,517 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: a49d6cf99e8d8e7a8e93668d09ced0bbdb954d0b4fccc2f5f9241c6b87fad95c
url: "https://pub.dev"
source: hosted
version: "99.0.0"
adaptive_number:
dependency: transitive
description:
name: adaptive_number
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "663efa951fb8a45e06f491223a604c93820598f20e6a99c25617a1576065e8b7"
url: "https://pub.dev"
source: hosted
version: "12.1.0"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.1"
bcrypt:
dependency: "direct main"
description:
name: bcrypt
sha256: "6073a700cbbc59f1d4ab27cd532755e3de5e676c4941f535f351374df849270b"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
buffer:
dependency: transitive
description:
name: buffer
sha256: "389da2ec2c16283c8787e0adaede82b1842102f8c8aae2f49003a766c5c6b3d1"
url: "https://pub.dev"
source: hosted
version: "1.2.3"
charcode:
dependency: transitive
description:
name: charcode
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
url: "https://pub.dev"
source: hosted
version: "1.4.0"
cli_config:
dependency: transitive
description:
name: cli_config
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
url: "https://pub.dev"
source: hosted
version: "0.2.0"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d"
url: "https://pub.dev"
source: hosted
version: "1.15.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
url: "https://pub.dev"
source: hosted
version: "3.0.7"
dart_jsonwebtoken:
dependency: "direct main"
description:
name: dart_jsonwebtoken
sha256: "00a0812d2aeaeb0d30bcbc4dd3cee57971dbc0ab2216adf4f0247f37793f15ef"
url: "https://pub.dev"
source: hosted
version: "2.17.0"
dotenv:
dependency: "direct main"
description:
name: dotenv
sha256: "379e64b6fc82d3df29461d349a1796ecd2c436c480d4653f3af6872eccbc90e1"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
ed25519_edwards:
dependency: transitive
description:
name: ed25519_edwards
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
http:
dependency: "direct dev"
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev"
source: hosted
version: "1.6.0"
http_methods:
dependency: transitive
description:
name: http_methods
sha256: "6bccce8f1ec7b5d701e7921dca35e202d425b57e317ba1a37f2638590e29e566"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
lints:
dependency: "direct dev"
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
url: "https://pub.dev"
source: hosted
version: "0.12.20"
meta:
dependency: transitive
description:
name: meta
sha256: df0c643f44ad098eb37988027a8e2b2b5a031fd3977f06bbfd3a76637e8df739
url: "https://pub.dev"
source: hosted
version: "1.18.2"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.2.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
postgres:
dependency: "direct main"
description:
name: postgres
sha256: "3af8a28b89fef68ee5b26b4fd27254d5a286389e9c7fb6293a5f46e30490f800"
url: "https://pub.dev"
source: hosted
version: "3.5.10"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shelf:
dependency: "direct main"
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_router:
dependency: "direct main"
description:
name: shelf_router
sha256: f5e5d492440a7fb165fe1e2e1a623f31f734d3370900070b2b1e0d0428d59864
url: "https://pub.dev"
source: hosted
version: "1.1.4"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test:
dependency: "direct dev"
description:
name: test
sha256: ca578dc12bb8b2f40b67b7d3bd2fac4f31c01a6ff7130a14e2597b919934507f
url: "https://pub.dev"
source: hosted
version: "1.31.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
url: "https://pub.dev"
source: hosted
version: "0.7.12"
test_core:
dependency: transitive
description:
name: test_core
sha256: d2e98ec12998368dc59ddd47ab709f2cd55acd6b66dc7db764455a44082f4bc5
url: "https://pub.dev"
source: hosted
version: "0.6.18"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
uuid:
dependency: "direct main"
description:
name: uuid
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
url: "https://pub.dev"
source: hosted
version: "4.5.3"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.10.1 <4.0.0"
+21
View File
@@ -0,0 +1,21 @@
name: family_safety_tracker
description: A server app using the shelf package and Docker.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
environment:
sdk: ^3.10.1
dependencies:
shelf: ^1.4.2
shelf_router: ^1.1.2
dart_jsonwebtoken: ^2.16.0
bcrypt: ^1.2.0
dotenv: ^4.1.0
postgres: ^3.5.10
uuid: ^4.5.0
dev_dependencies:
http: ^1.2.2
lints: ^6.0.0
test: ^1.25.6
+39
View File
@@ -0,0 +1,39 @@
import 'dart:io';
import 'package:http/http.dart';
import 'package:test/test.dart';
void main() {
final port = '8080';
final host = 'http://0.0.0.0:$port';
late Process p;
setUp(() async {
p = await Process.start(
'dart',
['run', 'bin/server.dart'],
environment: {'PORT': port},
);
// Wait for server to start and print to stdout.
await p.stdout.first;
});
tearDown(() => p.kill());
test('Root', () async {
final response = await get(Uri.parse('$host/'));
expect(response.statusCode, 200);
expect(response.body, 'Hello, World!\n');
});
test('Echo', () async {
final response = await get(Uri.parse('$host/echo/hello'));
expect(response.statusCode, 200);
expect(response.body, 'hello\n');
});
test('404', () async {
final response = await get(Uri.parse('$host/foobar'));
expect(response.statusCode, 404);
});
}
+5
View File
@@ -0,0 +1,5 @@
import 'package:bcrypt/bcrypt.dart';
void main() {
print(BCrypt);
}