Compare commits
13 Commits
04b7f3fbbf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e756cf8ba6 | |||
| 82c4e6875b | |||
| 58d3b26440 | |||
| 2349cb59d3 | |||
| 8c5f735697 | |||
| 6a9c00219c | |||
| 697dbe8d03 | |||
| a477f7dffc | |||
| e06816016d | |||
| 710a0b6f8e | |||
| b05e77aae1 | |||
| 77c1a31ffa | |||
| 45e42ca434 |
@@ -41,7 +41,10 @@ jobs:
|
||||
run: pnpm lint
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm test
|
||||
run: |
|
||||
pnpm exec playwright install
|
||||
pnpm exec playwright install-deps
|
||||
pnpm test
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
run: export BETTER_AUTH_SECRET="place-holder-for-build" && pnpm build
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,5 +2,7 @@
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
.idea
|
||||
|
||||
data
|
||||
cache
|
||||
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
1574
.idea/caches/deviceStreaming.xml
generated
1574
.idea/caches/deviceStreaming.xml
generated
File diff suppressed because it is too large
Load Diff
420
.idea/libraries/Dart_Packages.xml
generated
420
.idea/libraries/Dart_Packages.xml
generated
@@ -1,420 +0,0 @@
|
||||
<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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/_fe_analyzer_shared-96.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="analyzer">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/analyzer-10.2.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="args">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/args-2.7.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="async">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/async-2.13.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="boolean_selector">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/boolean_selector-2.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="cli_config">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/cli_config-0.2.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="collection">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/collection-1.19.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="convert">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/convert-3.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="coverage">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/coverage-1.15.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="crypto">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/crypto-3.0.7/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="file">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/file-7.0.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="frontend_server_client">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/frontend_server_client-4.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="glob">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/glob-2.1.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/http-1.6.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http_methods">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/http_parser-4.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="io">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/io-1.0.5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="lints">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/lints-6.1.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="logging">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/logging-1.3.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="matcher">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/matcher-0.12.19/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="meta">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/meta-1.18.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="mime">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/mime-2.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="node_preamble">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/node_preamble-2.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="package_config">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/package_config-2.2.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/path-1.9.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pool">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/pool-1.5.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pub_semver">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/pub_semver-2.2.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf-1.4.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf_packages_handler">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf_router-1.1.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="shelf_static">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/source_maps-0.10.13/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="source_span">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/source_span-1.10.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="stack_trace">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/stack_trace-1.12.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="stream_channel">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/stream_channel-2.1.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="string_scanner">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/string_scanner-1.4.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="term_glyph">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/term_glyph-1.2.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="test">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/test-1.30.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="test_api">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/test_api-0.7.10/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="test_core">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/test_core-0.6.16/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="typed_data">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/typed_data-1.4.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vm_service">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/vm_service-15.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="watcher">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/watcher-1.2.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/web-1.1.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="web_socket">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/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/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/webkit_inspection_protocol-1.2.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="yaml">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/yaml-3.1.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
</option>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/_fe_analyzer_shared-96.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/analyzer-10.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/args-2.7.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/async-2.13.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/boolean_selector-2.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/cli_config-0.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/collection-1.19.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/convert-3.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/coverage-1.15.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/crypto-3.0.7/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/file-7.0.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/frontend_server_client-4.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/glob-2.1.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/http-1.6.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/http_methods-1.1.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/http_multi_server-3.2.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/http_parser-4.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/io-1.0.5/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/lints-6.1.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/logging-1.3.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/matcher-0.12.19/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/meta-1.18.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/mime-2.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/node_preamble-2.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/package_config-2.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/path-1.9.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/pool-1.5.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/pub_semver-2.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf-1.4.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf_packages_handler-3.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf_router-1.1.4/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf_static-1.1.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/shelf_web_socket-3.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/source_map_stack_trace-2.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/source_maps-0.10.13/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/source_span-1.10.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/stack_trace-1.12.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/stream_channel-2.1.4/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/string_scanner-1.4.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/term_glyph-1.2.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/test-1.30.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/test_api-0.7.10/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/test_core-0.6.16/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/typed_data-1.4.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/vm_service-15.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/watcher-1.2.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/web-1.1.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/web_socket-1.0.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/web_socket_channel-3.0.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/webkit_inspection_protocol-1.2.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/mirrors.tuna.tsinghua.edu.cn%47dart-pub%47/yaml-3.1.3/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
31
.idea/libraries/Dart_SDK.xml
generated
31
.idea/libraries/Dart_SDK.xml
generated
@@ -1,31 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Dart SDK">
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/_internal" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/async" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/cli" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/collection" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/concurrent" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/convert" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/core" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/developer" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/ffi" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/html" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/indexed_db" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/io" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/isolate" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/js" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/js_interop" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/js_interop_unsafe" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/js_util" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/math" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/mirrors" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/svg" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/typed_data" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/web_audio" />
|
||||
<root url="file://$PROJECT_DIR$/../../../Repository/Scoop/apps/dart/3.10.7/lib/web_gl" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
8
.idea/markdown.xml
generated
8
.idea/markdown.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@@ -1,5 +0,0 @@
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/loongyan.iml" filepath="$PROJECT_DIR$/loongyan.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/studiobot.xml
generated
6
.idea/studiobot.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="StudioBotProjectSettings">
|
||||
<option name="shareContext" value="OptedOut" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
3
web/.gitignore
vendored
3
web/.gitignore
vendored
@@ -26,3 +26,6 @@ src/lib/paraglide
|
||||
project.inlang/cache/
|
||||
# SQLite
|
||||
*.db
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
10
web/.idea/.gitignore
generated
vendored
10
web/.idea/.gitignore
generated
vendored
@@ -1,10 +0,0 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 已忽略包含查询文件的默认文件夹
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
63
web/.idea/codeStyles/Project.xml
generated
63
web/.idea/codeStyles/Project.xml
generated
@@ -1,63 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
web/.idea/codeStyles/codeStyleConfig.xml
generated
5
web/.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
web/.idea/inspectionProfiles/Project_Default.xml
generated
6
web/.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
8
web/.idea/modules.xml
generated
8
web/.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/web.iml" filepath="$PROJECT_DIR$/.idea/web.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
web/.idea/prettier.xml
generated
6
web/.idea/prettier.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
</component>
|
||||
</project>
|
||||
6
web/.idea/vcs.xml
generated
6
web/.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
10
web/.idea/web.iml
generated
10
web/.idea/web.iml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.svelte-kit" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
3
web/.vscode/extensions.json
vendored
3
web/.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
|
||||
}
|
||||
@@ -23,18 +23,18 @@ Loongyan 相册系统的前端应用,基于 [SvelteKit](https://kit.svelte.dev
|
||||
|
||||
## 🛠️ 技术栈
|
||||
|
||||
| 技术 | 说明 |
|
||||
|------|------|
|
||||
| [Svelte 5](https://svelte.dev/) | 前端框架(Runes 模式) |
|
||||
| [SvelteKit 2](https://kit.svelte.dev/) | 全栈框架 |
|
||||
| [Vite 7](https://vitejs.dev/) | 构建工具 |
|
||||
| [Paraglide JS](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) | 国际化 |
|
||||
| [Better Auth](https://www.better-auth.com/) | 用户认证 |
|
||||
| [Drizzle ORM](https://orm.drizzle.team/) | 数据库 ORM |
|
||||
| [SQLite](https://www.sqlite.org/) | 嵌入式数据库 |
|
||||
| [Vitest](https://vitest.dev/) | 单元测试 |
|
||||
| [Playwright](https://playwright.dev/) | 浏览器测试 |
|
||||
| [mdsvex](https://mdsvex.pngwn.io/) | Markdown 组件 |
|
||||
| 技术 | 说明 |
|
||||
| ------------------------------------------------------------------------ | ---------------------- |
|
||||
| [Svelte 5](https://svelte.dev/) | 前端框架(Runes 模式) |
|
||||
| [SvelteKit 2](https://kit.svelte.dev/) | 全栈框架 |
|
||||
| [Vite 7](https://vitejs.dev/) | 构建工具 |
|
||||
| [Paraglide JS](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) | 国际化 |
|
||||
| [Better Auth](https://www.better-auth.com/) | 用户认证 |
|
||||
| [Drizzle ORM](https://orm.drizzle.team/) | 数据库 ORM |
|
||||
| [SQLite](https://www.sqlite.org/) | 嵌入式数据库 |
|
||||
| [Vitest](https://vitest.dev/) | 单元测试 |
|
||||
| [Playwright](https://playwright.dev/) | 浏览器测试 |
|
||||
| [mdsvex](https://mdsvex.pngwn.io/) | Markdown 组件 |
|
||||
|
||||
---
|
||||
|
||||
@@ -139,14 +139,14 @@ const API_BASE = '/api/v1';
|
||||
|
||||
### 可用 API 方法
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `getAlbums()` | 获取所有相册 |
|
||||
| `getAlbum(id)` | 获取相册详情 |
|
||||
| 方法 | 说明 |
|
||||
| ------------------------- | -------------- |
|
||||
| `getAlbums()` | 获取所有相册 |
|
||||
| `getAlbum(id)` | 获取相册详情 |
|
||||
| `getAlbumPhotos(albumId)` | 获取相册内照片 |
|
||||
| `getPhoto(id)` | 获取照片详情 |
|
||||
| `getPhotoFileUrl(id)` | 获取原图 URL |
|
||||
| `getPhotoPreviewUrl(id)` | 获取预览图 URL |
|
||||
| `getPhoto(id)` | 获取照片详情 |
|
||||
| `getPhotoFileUrl(id)` | 获取原图 URL |
|
||||
| `getPhotoPreviewUrl(id)` | 获取预览图 URL |
|
||||
|
||||
---
|
||||
|
||||
@@ -179,24 +179,24 @@ pnpm check:watch
|
||||
|
||||
### 基础 UI 组件
|
||||
|
||||
| 组件 | 说明 |
|
||||
|------|------|
|
||||
| `Button` | 按钮组件 |
|
||||
| `Card` | 卡片容器 |
|
||||
| `Container` | 页面容器 |
|
||||
| `Grid` | 网格布局 |
|
||||
| `Loading` | 加载状态 |
|
||||
| `Empty` | 空状态提示 |
|
||||
| 组件 | 说明 |
|
||||
| ----------- | ---------- |
|
||||
| `Button` | 按钮组件 |
|
||||
| `Card` | 卡片容器 |
|
||||
| `Container` | 页面容器 |
|
||||
| `Grid` | 网格布局 |
|
||||
| `Loading` | 加载状态 |
|
||||
| `Empty` | 空状态提示 |
|
||||
|
||||
### 业务组件
|
||||
|
||||
| 组件 | 说明 |
|
||||
|------|------|
|
||||
| `AlbumCard` | 相册卡片 |
|
||||
| `AlbumList` | 相册列表 |
|
||||
| `PhotoCard` | 照片卡片 |
|
||||
| `PhotoGrid` | 照片网格 |
|
||||
| `BackLink` | 返回链接 |
|
||||
| 组件 | 说明 |
|
||||
| ------------ | -------- |
|
||||
| `AlbumCard` | 相册卡片 |
|
||||
| `AlbumList` | 相册列表 |
|
||||
| `PhotoCard` | 照片卡片 |
|
||||
| `PhotoGrid` | 照片网格 |
|
||||
| `BackLink` | 返回链接 |
|
||||
| `PageHeader` | 页面标题 |
|
||||
|
||||
---
|
||||
@@ -228,7 +228,7 @@ pnpm check:watch
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
import * as m from '$lib/paraglide/messages';
|
||||
</script>
|
||||
|
||||
<h1>{m.welcome()}</h1>
|
||||
@@ -262,16 +262,16 @@ pnpm drizzle-kit migrate
|
||||
|
||||
## 📜 脚本命令
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `pnpm dev` | 启动开发服务器 |
|
||||
| `pnpm build` | 构建生产版本 |
|
||||
| `pnpm preview` | 预览生产构建 |
|
||||
| `pnpm test` | 运行测试 |
|
||||
| `pnpm test:unit` | 运行单元测试 |
|
||||
| `pnpm check` | 类型检查 |
|
||||
| `pnpm lint` | 代码检查 |
|
||||
| `pnpm format` | 格式化代码 |
|
||||
| 命令 | 说明 |
|
||||
| ------------------ | --------------- |
|
||||
| `pnpm dev` | 启动开发服务器 |
|
||||
| `pnpm build` | 构建生产版本 |
|
||||
| `pnpm preview` | 预览生产构建 |
|
||||
| `pnpm test` | 运行测试 |
|
||||
| `pnpm test:unit` | 运行单元测试 |
|
||||
| `pnpm check` | 类型检查 |
|
||||
| `pnpm lint` | 代码检查 |
|
||||
| `pnpm format` | 格式化代码 |
|
||||
| `pnpm auth:schema` | 生成认证 Schema |
|
||||
|
||||
---
|
||||
@@ -304,9 +304,9 @@ pnpm add -D @sveltejs/adapter-static
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
|
||||
const config = {
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
}
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
@@ -322,4 +322,4 @@ const config = {
|
||||
|
||||
<div align="center">
|
||||
<sub>Made with ❤️ using SvelteKit</sub>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,21 +13,17 @@
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write .",
|
||||
"test:unit": "vitest",
|
||||
"test": "npm run test:unit -- --run",
|
||||
"auth:schema": "better-auth generate --config src/lib/server/auth.js --output src/lib/server/db/auth.schema.js --yes"
|
||||
"test": "npm run test:unit -- --run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@better-auth/cli": "~1.4.21",
|
||||
"@eslint/compat": "^2.0.2",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@inlang/paraglide-js": "^2.10.0",
|
||||
"@sveltejs/adapter-node": "^5.2.12",
|
||||
"@sveltejs/kit": "^2.50.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/node": "^22",
|
||||
"@vitest/browser-playwright": "^4.0.18",
|
||||
"better-auth": "~1.4.21",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.14.0",
|
||||
@@ -46,6 +42,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "^5.2.8",
|
||||
"@fontsource-variable/noto-sans-sc": "^5.2.10"
|
||||
"@fontsource-variable/noto-sans-sc": "^5.2.10",
|
||||
"@sveltejs/adapter-static": "^3.0.10"
|
||||
}
|
||||
}
|
||||
|
||||
2320
web/pnpm-lock.yaml
generated
2320
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
6
web/src/app.d.ts
vendored
6
web/src/app.d.ts
vendored
@@ -1,12 +1,12 @@
|
||||
import type { User, Session } from 'better-auth/minimal';
|
||||
// import type { User, Session } from 'better-auth/minimal';
|
||||
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
user?: User;
|
||||
session?: Session;
|
||||
// user?: User;
|
||||
// session?: Session;
|
||||
}
|
||||
|
||||
// interface Error {}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { sequence } from '@sveltejs/kit/hooks';
|
||||
import { building } from '$app/environment';
|
||||
import { auth } from '$lib/server/auth';
|
||||
import { svelteKitHandler } from 'better-auth/svelte-kit';
|
||||
import { getTextDirection } from '$lib/paraglide/runtime';
|
||||
import { paraglideMiddleware } from '$lib/paraglide/server';
|
||||
import { env } from '$env/dynamic/private';
|
||||
@@ -19,20 +16,6 @@ const handleParaglide = ({ event, resolve }) =>
|
||||
});
|
||||
});
|
||||
|
||||
/** @type {import('@sveltejs/kit').Handle} */
|
||||
const handleBetterAuth = async ({ event, resolve }) => {
|
||||
const session = await auth.api.getSession({
|
||||
/** @type {import('@sveltejs/kit').Handle} */ headers: event.request.headers
|
||||
});
|
||||
|
||||
if (session) {
|
||||
event.locals.session = session.session;
|
||||
event.locals.user = session.user;
|
||||
}
|
||||
|
||||
return svelteKitHandler({ event, resolve, auth, building });
|
||||
};
|
||||
|
||||
/**
|
||||
* API 代理处理 - 将 /api/v1 请求代理到 Dart 后端
|
||||
* 仅在生产环境生效,开发环境由 Vite proxy 处理
|
||||
@@ -49,7 +32,10 @@ const handleApiProxy = async ({ event, resolve }) => {
|
||||
const backendResponse = await fetch(targetUrl, {
|
||||
method: request.method,
|
||||
headers: request.headers,
|
||||
body: request.method !== 'GET' && request.method !== 'HEAD' ? await request.arrayBuffer() : undefined
|
||||
body:
|
||||
request.method !== 'GET' && request.method !== 'HEAD'
|
||||
? await request.arrayBuffer()
|
||||
: undefined
|
||||
});
|
||||
|
||||
// 返回后端响应
|
||||
@@ -96,4 +82,4 @@ const handleCache = async ({ event, resolve }) => {
|
||||
return newResponse;
|
||||
};
|
||||
|
||||
export const handle = sequence(handleApiProxy, handleParaglide, handleBetterAuth, handleCache);
|
||||
export const handle = sequence(handleApiProxy, handleParaglide, handleCache);
|
||||
|
||||
@@ -50,11 +50,9 @@ export async function getAlbumPhotos(albumId, options = {}) {
|
||||
if (options.size) params.set('size', String(options.size));
|
||||
if (options.sort) params.set('sort', String(options.sort));
|
||||
if (options.order) params.set('order', String(options.order));
|
||||
|
||||
|
||||
const query = params.toString();
|
||||
const endpoint = query
|
||||
? `/album/${albumId}/photo?${query}`
|
||||
: `/album/${albumId}/photo`;
|
||||
const endpoint = query ? `/album/${albumId}/photo?${query}` : `/album/${albumId}/photo`;
|
||||
return fetchApi(endpoint);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,15 @@
|
||||
*/
|
||||
|
||||
/** @type {PhotoCardProps} */
|
||||
let { photo, onUpgradeQuality, borderLess = false, waterfall = false, index = 0, sortMode = 'fileName', sortOrder = 'asc' } = $props();
|
||||
let {
|
||||
photo,
|
||||
onUpgradeQuality,
|
||||
borderLess = false,
|
||||
waterfall = false,
|
||||
index = 0,
|
||||
sortMode = 'fileName',
|
||||
sortOrder = 'asc'
|
||||
} = $props();
|
||||
|
||||
// 使用 $derived 确保响应式更新
|
||||
let previewSrc = $derived(`/api/v1/photo/${photo.id}/preview`);
|
||||
@@ -23,9 +31,7 @@
|
||||
|
||||
// 根据原始宽高比计算瀑布流显示比例,防止图片加载前容器塌陷
|
||||
let aspectRatioStyle = $derived(
|
||||
waterfall && photo.width && photo.height
|
||||
? `${photo.width} / ${photo.height}`
|
||||
: ''
|
||||
waterfall && photo.width && photo.height ? `${photo.width} / ${photo.height}` : ''
|
||||
);
|
||||
|
||||
// 砖块模式:交错延迟(按网格行顺序);瀑布模式:同时入场(避免列填充顺序与延迟不匹配)
|
||||
@@ -44,7 +50,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<a href={photoLink} class="photo-card" class:photo-card-waterfall={waterfall} class:photo-card-compact-waterfall={borderLess && waterfall} style={`--wf-aspect: ${aspectRatioStyle}; --wf-delay: ${animationDelay};`}>
|
||||
<a
|
||||
href={resolve(photoLink)}
|
||||
class="photo-card"
|
||||
class:photo-card-waterfall={waterfall}
|
||||
class:photo-card-compact-waterfall={borderLess && waterfall}
|
||||
style={`--wf-aspect: ${aspectRatioStyle}; --wf-delay: ${animationDelay};`}
|
||||
>
|
||||
<div class="photo-wrapper" class:photo-borderless={borderLess} class:photo-waterfall={waterfall}>
|
||||
{#if photo.mimeType?.startsWith('video/')}
|
||||
<div class="video-indicator">🎬</div>
|
||||
@@ -70,13 +82,13 @@
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.photo-card {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
.photo-card {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
animation: card-enter 0.35s cubic-bezier(0.4, 0, 0.2, 1) both;
|
||||
animation-delay: var(--wf-delay, 0ms);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes card-enter {
|
||||
from {
|
||||
@@ -100,16 +112,18 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.photo-wrapper {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
background: linear-gradient(135deg, var(--color-bg-tertiary) 0%, var(--color-border) 100%);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--space-sm);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: border-radius 0.25s ease-out, margin-bottom 0.25s ease-out;
|
||||
}
|
||||
.photo-wrapper {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
background: linear-gradient(135deg, var(--color-bg-tertiary) 0%, var(--color-border) 100%);
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--space-sm);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition:
|
||||
border-radius 0.25s ease-out,
|
||||
margin-bottom 0.25s ease-out;
|
||||
}
|
||||
|
||||
.photo-borderless {
|
||||
border-radius: 0;
|
||||
@@ -123,13 +137,13 @@
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.photo-wrapper img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform var(--transition-slow);
|
||||
background: var(--color-border);
|
||||
}
|
||||
.photo-wrapper img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform var(--transition-slow);
|
||||
background: var(--color-border);
|
||||
}
|
||||
|
||||
/* 瀑布流模式下图片高度自适应 */
|
||||
.photo-waterfall img {
|
||||
@@ -138,51 +152,52 @@
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.photo-card:hover .photo-wrapper img {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
.photo-card:hover .photo-wrapper img {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.photo-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
padding: var(--space-md);
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
}
|
||||
.photo-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
padding: var(--space-md);
|
||||
text-align: center;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 瀑布流模式下 placeholder 默认 4:3 */
|
||||
.photo-waterfall .photo-placeholder {
|
||||
aspect-ratio: 4/3;
|
||||
}
|
||||
|
||||
.video-indicator {
|
||||
position: absolute;
|
||||
top: var(--space-sm);
|
||||
right: var(--space-sm);
|
||||
background: var(--color-overlay-dark);
|
||||
color: var(--color-text-inverse);
|
||||
padding: 0.35rem var(--space-sm);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-sm);
|
||||
z-index: 1;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.video-indicator {
|
||||
position: absolute;
|
||||
top: var(--space-sm);
|
||||
right: var(--space-sm);
|
||||
background: var(--color-overlay-dark);
|
||||
color: var(--color-text-inverse);
|
||||
padding: 0.35rem var(--space-sm);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-sm);
|
||||
z-index: 1;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.photo-name {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text);
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 0 var(--space-xs);
|
||||
.photo-name {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text);
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 0 var(--space-xs);
|
||||
transition: visibility var(--transition-slow) cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.photo-name-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -101,10 +101,17 @@
|
||||
{#key waterfall + '_' + borderLess + '_' + columnCount}
|
||||
{#if waterfall}
|
||||
<div class="photo-grid-waterfall" class:photo-grid-borderless={borderLess}>
|
||||
{#each splitToColumns(getVisiblePhotos(), columnCount) as col}
|
||||
{#each splitToColumns(getVisiblePhotos(), columnCount) as col (col)}
|
||||
<div class="waterfall-column">
|
||||
{#each col as photo (photo.id)}
|
||||
<PhotoCard {photo} {onUpgradeQuality} {borderLess} {waterfall} {sortMode} {sortOrder} />
|
||||
<PhotoCard
|
||||
{photo}
|
||||
{onUpgradeQuality}
|
||||
{borderLess}
|
||||
{waterfall}
|
||||
{sortMode}
|
||||
{sortOrder}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
@@ -112,7 +119,15 @@
|
||||
{:else}
|
||||
<div class="photo-grid" class:photo-grid-borderless={borderLess}>
|
||||
{#each getVisiblePhotos() as photo, i (photo.id)}
|
||||
<PhotoCard {photo} {onUpgradeQuality} {borderLess} {waterfall} index={i} {sortMode} {sortOrder} />
|
||||
<PhotoCard
|
||||
{photo}
|
||||
{onUpgradeQuality}
|
||||
{borderLess}
|
||||
{waterfall}
|
||||
index={i}
|
||||
{sortMode}
|
||||
{sortOrder}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -135,98 +150,98 @@
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.photo-scroll-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scroll-behavior: smooth;
|
||||
min-height: 400px;
|
||||
}
|
||||
.photo-scroll-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scroll-behavior: smooth;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.photo-scroll-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
.photo-scroll-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.photo-scroll-container::-webkit-scrollbar-track {
|
||||
background: var(--color-bg-tertiary);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.photo-scroll-container::-webkit-scrollbar-track {
|
||||
background: var(--color-bg-tertiary);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.photo-scroll-container::-webkit-scrollbar-thumb {
|
||||
background: var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.photo-scroll-container::-webkit-scrollbar-thumb {
|
||||
background: var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.photo-scroll-container::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-text-tertiary);
|
||||
}
|
||||
.photo-scroll-container::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
.photo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: var(--space-md);
|
||||
padding-bottom: var(--space-xl);
|
||||
transition: gap 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.photo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: var(--space-md);
|
||||
padding-bottom: var(--space-xl);
|
||||
transition: gap 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.photo-grid-borderless {
|
||||
gap: 0;
|
||||
}
|
||||
.photo-grid-borderless {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* 瀑布流布局:Flex 列分栏 */
|
||||
.photo-grid-waterfall {
|
||||
display: flex;
|
||||
gap: var(--space-md);
|
||||
padding-bottom: var(--space-xl);
|
||||
}
|
||||
/* 瀑布流布局:Flex 列分栏 */
|
||||
.photo-grid-waterfall {
|
||||
display: flex;
|
||||
gap: var(--space-md);
|
||||
padding-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.photo-grid-waterfall.photo-grid-borderless {
|
||||
gap: 0;
|
||||
}
|
||||
.photo-grid-waterfall.photo-grid-borderless {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.waterfall-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
.waterfall-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.photo-grid-waterfall .waterfall-column > :global(.photo-card) {
|
||||
break-inside: avoid;
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
.photo-grid-waterfall .waterfall-column > :global(.photo-card) {
|
||||
break-inside: avoid;
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
|
||||
.photo-grid-waterfall .waterfall-column > :global(.photo-card):last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.photo-grid-waterfall .waterfall-column > :global(.photo-card):last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.photo-grid-waterfall.photo-grid-borderless .waterfall-column > :global(.photo-card) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.photo-grid-waterfall.photo-grid-borderless .waterfall-column > :global(.photo-card) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.photo-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.photo-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.load-more-trigger,
|
||||
.loading-trigger {
|
||||
text-align: center;
|
||||
padding: var(--space-xl);
|
||||
}
|
||||
.load-more-trigger,
|
||||
.loading-trigger {
|
||||
text-align: center;
|
||||
padding: var(--space-xl);
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: inline-block;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
.loading-more {
|
||||
display: inline-block;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
|
||||
.load-complete {
|
||||
text-align: center;
|
||||
padding: var(--space-xl);
|
||||
color: var(--color-success);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.load-complete {
|
||||
text-align: center;
|
||||
padding: var(--space-xl);
|
||||
color: var(--color-success);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { betterAuth } from 'better-auth/minimal';
|
||||
import { sveltekitCookies } from 'better-auth/svelte-kit';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { getRequestEvent } from '$app/server';
|
||||
|
||||
export const auth = betterAuth({
|
||||
baseURL: env.ORIGIN,
|
||||
secret: env.BETTER_AUTH_SECRET,
|
||||
// database: drizzleAdapter(db, { provider: 'sqlite' }),
|
||||
emailAndPassword: { enabled: true },
|
||||
plugins: [sveltekitCookies(getRequestEvent)] // make sure this is the last plugin in the array
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
// If you see this file, you have not run the auth:schema script yet, but you should!
|
||||
@@ -1,10 +0,0 @@
|
||||
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
||||
import Database from 'better-sqlite3';
|
||||
import * as schema from './schema';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
|
||||
|
||||
const client = new Database(env.DATABASE_URL);
|
||||
|
||||
export const db = drizzle(client, { schema });
|
||||
@@ -1,11 +0,0 @@
|
||||
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
export const task = sqliteTable('task', {
|
||||
id: text('id')
|
||||
.primaryKey()
|
||||
.$defaultFn(() => crypto.randomUUID()),
|
||||
title: text('title').notNull(),
|
||||
priority: integer('priority').notNull().default(1)
|
||||
});
|
||||
|
||||
export * from './auth.schema';
|
||||
@@ -55,7 +55,8 @@
|
||||
--radius-full: 9999px;
|
||||
|
||||
/* ========== Typography ========== */
|
||||
--font-family: 'Inter', 'Noto Sans SC', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--font-family:
|
||||
'Inter', 'Noto Sans SC', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--font-size-xs: 0.75rem; /* 12px */
|
||||
--font-size-sm: 0.875rem; /* 14px */
|
||||
--font-size-base: 1rem; /* 16px */
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function load({ params, fetch, url }) {
|
||||
|
||||
// 检测后端是否返回了分页信息
|
||||
const hasMore = photos?.items
|
||||
? (photos.page * photos.size) < photos.total
|
||||
? photos.page * photos.size < photos.total
|
||||
: photos?.length === pageSize;
|
||||
const totalPhotos = photos?.total ?? photos?.length ?? 0;
|
||||
|
||||
|
||||
@@ -2,15 +2,31 @@
|
||||
import { page } from '$app/state';
|
||||
import { browser } from '$app/environment';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { albums, back, photo_count, loading,
|
||||
style_card, style_compact,
|
||||
layout_grid, layout_waterfall,
|
||||
sort_name, sort_time, sort_size,
|
||||
sort_asc, sort_desc
|
||||
import {
|
||||
albums,
|
||||
back,
|
||||
photo_count,
|
||||
loading,
|
||||
style_card,
|
||||
style_compact,
|
||||
layout_grid,
|
||||
layout_waterfall,
|
||||
sort_name,
|
||||
sort_time,
|
||||
sort_size,
|
||||
sort_asc,
|
||||
sort_desc
|
||||
} from '$lib/paraglide/messages';
|
||||
import { resolve } from '$app/paths';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
import { Container, PageHeader, BackLink, PhotoGrid, Empty, SegmentedControl } from '$lib/components';
|
||||
import {
|
||||
Container,
|
||||
PageHeader,
|
||||
BackLink,
|
||||
PhotoGrid,
|
||||
Empty,
|
||||
SegmentedControl
|
||||
} from '$lib/components';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let { data } = $props();
|
||||
@@ -49,9 +65,11 @@
|
||||
let layoutMode = $state('grid');
|
||||
|
||||
// 排序模式
|
||||
let sortMode = $state(page.data.album ? (page.url.searchParams.get('sort') || 'fileName') : 'fileName');
|
||||
let sortMode = $state(
|
||||
page.data.album ? page.url.searchParams.get('sort') || 'fileName' : 'fileName'
|
||||
);
|
||||
// 排序方向
|
||||
let sortOrder = $state(page.data.album ? (page.url.searchParams.get('order') || 'asc') : 'asc');
|
||||
let sortOrder = $state(page.data.album ? page.url.searchParams.get('order') || 'asc' : 'asc');
|
||||
|
||||
const styleOptions = $derived([
|
||||
{ value: 'card', label: style_card(), icon: '🖼️' },
|
||||
@@ -133,7 +151,7 @@
|
||||
allPhotos = [...allPhotos, ...newPhotos];
|
||||
// 根据后端返回的 total 判断是否还有更多
|
||||
const total = newData.totalPhotos ?? 0;
|
||||
hasMore = (currentPage * PAGE_SIZE) < total;
|
||||
hasMore = currentPage * PAGE_SIZE < total;
|
||||
displayedCount += BATCH_SIZE;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -147,10 +165,15 @@
|
||||
// 处理排序变化
|
||||
function applySort() {
|
||||
// 重置分页状态并导航到新 URL
|
||||
goto(`${page.url.pathname}?page=1&pageSize=${PAGE_SIZE}&sort=${sortMode}&order=${sortOrder}`, {
|
||||
replaceState: true,
|
||||
keepFocus: true
|
||||
});
|
||||
goto(
|
||||
resolve(
|
||||
`${page.url.pathname}?page=1&pageSize=${PAGE_SIZE}&sort=${sortMode}&order=${sortOrder}`
|
||||
),
|
||||
{
|
||||
replaceState: true,
|
||||
keepFocus: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,38 +210,43 @@
|
||||
</svelte:head>
|
||||
|
||||
<Container size="wide">
|
||||
<PageHeader
|
||||
title={album?.name || loading()}
|
||||
>
|
||||
<PageHeader title={album?.name || loading()}>
|
||||
<BackLink href={resolve('/')} text={back()} />
|
||||
</PageHeader>
|
||||
|
||||
{#if album && allPhotos.length > 0}
|
||||
<div class="subtitle-bar">
|
||||
<span class="photo-count">{photo_count({ count: data.totalPhotos || allPhotos.length })}</span>
|
||||
<span class="photo-count">{photo_count({ count: data.totalPhotos || allPhotos.length })}</span
|
||||
>
|
||||
<div class="controls">
|
||||
<SegmentedControl
|
||||
value={styleMode}
|
||||
onchange={(v) => styleMode = v}
|
||||
onchange={(v) => (styleMode = v)}
|
||||
options={styleOptions}
|
||||
size="small"
|
||||
/>
|
||||
<SegmentedControl
|
||||
value={layoutMode}
|
||||
onchange={(v) => layoutMode = v}
|
||||
onchange={(v) => (layoutMode = v)}
|
||||
options={layoutOptions}
|
||||
size="small"
|
||||
/>
|
||||
<SegmentedControl
|
||||
value={sortMode}
|
||||
onchange={(v) => { sortMode = v; applySort(); }}
|
||||
onchange={(v) => {
|
||||
sortMode = v;
|
||||
applySort();
|
||||
}}
|
||||
options={sortOptions}
|
||||
size="small"
|
||||
/>
|
||||
<button
|
||||
class="sort-order-toggle"
|
||||
type="button"
|
||||
onclick={() => { sortOrder = sortOrder === 'asc' ? 'desc' : 'asc'; applySort(); }}
|
||||
onclick={() => {
|
||||
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
|
||||
applySort();
|
||||
}}
|
||||
aria-label={sortOrder === 'asc' ? sort_desc() : sort_asc()}
|
||||
>
|
||||
<span class="sort-icon">{sortOrder === 'asc' ? '↑' : '↓'}</span>
|
||||
@@ -241,8 +269,8 @@
|
||||
bind:loadMoreTrigger
|
||||
borderLess={styleMode === 'borderless'}
|
||||
waterfall={layoutMode === 'waterfall'}
|
||||
sortMode={sortMode}
|
||||
sortOrder={sortOrder}
|
||||
{sortMode}
|
||||
{sortOrder}
|
||||
/>
|
||||
{/if}
|
||||
</Container>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<a href={resolve('/demo/better-auth')}>better-auth</a>
|
||||
<a href={resolve('/demo/paraglide')}>paraglide</a>
|
||||
@@ -1,19 +0,0 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
import { auth } from '$lib/server/auth';
|
||||
|
||||
export const load = async (event) => {
|
||||
if (!event.locals.user) {
|
||||
return redirect(302, '/demo/better-auth/login');
|
||||
}
|
||||
return { user: event.locals.user };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
signOut: async (event) => {
|
||||
await auth.api.signOut({
|
||||
headers: event.request.headers
|
||||
});
|
||||
return redirect(302, '/demo/better-auth/login');
|
||||
}
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
<script>
|
||||
import { enhance } from '$app/forms';
|
||||
|
||||
let { data } = $props();
|
||||
</script>
|
||||
|
||||
<h1>Hi, {data.user.name}!</h1>
|
||||
<p>Your user ID is {data.user.id}.</p>
|
||||
<form method="post" action="?/signOut" use:enhance>
|
||||
<button>Sign out</button>
|
||||
</form>
|
||||
@@ -1,60 +0,0 @@
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
|
||||
import { auth } from '$lib/server/auth';
|
||||
import { APIError } from 'better-auth/api';
|
||||
|
||||
export const load = async (event) => {
|
||||
if (event.locals.user) {
|
||||
return redirect(302, '/demo/better-auth');
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
signInEmail: async (event) => {
|
||||
const formData = await event.request.formData();
|
||||
const email = formData.get('email')?.toString() ?? '';
|
||||
const password = formData.get('password')?.toString() ?? '';
|
||||
|
||||
try {
|
||||
await auth.api.signInEmail({
|
||||
body: {
|
||||
email,
|
||||
password,
|
||||
callbackURL: '/auth/verification-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof APIError) {
|
||||
return fail(400, { message: error.message || 'Signin failed' });
|
||||
}
|
||||
return fail(500, { message: 'Unexpected error' });
|
||||
}
|
||||
|
||||
return redirect(302, '/demo/better-auth');
|
||||
},
|
||||
signUpEmail: async (event) => {
|
||||
const formData = await event.request.formData();
|
||||
const email = formData.get('email')?.toString() ?? '';
|
||||
const password = formData.get('password')?.toString() ?? '';
|
||||
const name = formData.get('name')?.toString() ?? '';
|
||||
|
||||
try {
|
||||
await auth.api.signUpEmail({
|
||||
body: {
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
callbackURL: '/auth/verification-success'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof APIError) {
|
||||
return fail(400, { message: error.message || 'Registration failed' });
|
||||
}
|
||||
return fail(500, { message: 'Unexpected error' });
|
||||
}
|
||||
|
||||
return redirect(302, '/demo/better-auth');
|
||||
}
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
<script>
|
||||
import { enhance } from '$app/forms';
|
||||
|
||||
let { form } = $props();
|
||||
</script>
|
||||
|
||||
<h1>Login</h1>
|
||||
<form method="post" action="?/signInEmail" use:enhance>
|
||||
<label>
|
||||
Email
|
||||
<input type="email" name="email" />
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" />
|
||||
</label>
|
||||
<label>
|
||||
Name (for registration)
|
||||
<input name="name" />
|
||||
</label>
|
||||
<button>Login</button>
|
||||
<button formaction="?/signUpEmail">Register</button>
|
||||
</form>
|
||||
<p style="color: red">{form?.message ?? ''}</p>
|
||||
@@ -1,22 +0,0 @@
|
||||
<script>
|
||||
import { setLocale } from '$lib/paraglide/runtime';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
</script>
|
||||
|
||||
<h1>{m.hello_world({ name: 'SvelteKit User' })}</h1>
|
||||
|
||||
<div>
|
||||
<button onclick={() => setLocale('en')}>en</button>
|
||||
<button onclick={() => setLocale('zh')}>zh</button>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
If you use VSCode, install the
|
||||
|
||||
<a
|
||||
href="https://marketplace.visualstudio.com/items?itemName=inlang.vs-code-extension"
|
||||
target="_blank">Sherlock i18n extension</a
|
||||
>
|
||||
|
||||
for a better i18n experience.
|
||||
</p>
|
||||
@@ -37,7 +37,7 @@ export async function load({ params, fetch, url }) {
|
||||
`/album/${photo.albumId}/photo?sort=${sort}&order=${order}`,
|
||||
fetch
|
||||
);
|
||||
const photos = Array.isArray(albumPhotos) ? albumPhotos : albumPhotos?.items ?? [];
|
||||
const photos = Array.isArray(albumPhotos) ? albumPhotos : (albumPhotos?.items ?? []);
|
||||
|
||||
if (photos.length > 0) {
|
||||
const index = photos.findIndex((p) => String(p.id) === String(photoId));
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { mdsvex } from 'mdsvex';
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
// 前端服务端口,通过环境变量配置
|
||||
env: { port: process.env.PORT ?? '3000', host: process.env.HOST ?? '0.0.0.0' }
|
||||
env: { port: process.env.PORT ?? '3000', host: process.env.HOST ?? '0.0.0.0' },
|
||||
fallback: 'index.html'
|
||||
})
|
||||
},
|
||||
vitePlugin: {
|
||||
@@ -16,4 +17,4 @@ const config = {
|
||||
extensions: ['.svelte', '.svx']
|
||||
};
|
||||
|
||||
export default config;
|
||||
export default config;
|
||||
|
||||
Reference in New Issue
Block a user