Compare commits

...

11 Commits

Author SHA1 Message Date
187b42e465 dependency update
All checks were successful
Build and Push Multi-Arch Docker Image / build-and-push (push) Successful in 11m21s
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-28 22:09:42 +01:00
227311a39e Revert "updated Dockerfile for custom user"
This reverts commit 0e46299ee0.
2025-11-28 22:09:12 +01:00
0e46299ee0 updated Dockerfile for custom user
Some checks failed
Build and Push Multi-Arch Docker Image / build-and-push (push) Failing after 1m4s
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-28 22:07:34 +01:00
d7ce320702 new login logic
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-28 22:06:53 +01:00
4ca90e327f try new container build
All checks were successful
Build and Push Multi-Arch Docker Image / build-and-push (push) Successful in 11m23s
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-28 21:15:23 +01:00
4defe266eb latest server code
Some checks failed
Build and Push Multi-Arch Docker Image / build-and-push (push) Failing after 38s
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-28 21:02:02 +01:00
8f51ac8b24 try gitea actions for dart server
Some checks failed
Build and Push Multi-Arch Docker Image / build-and-push (push) Failing after 1m46s
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-21 21:02:10 +01:00
4ce471599b load trending images from tmdb at startup
- need TMDB_API_KEY as environment variable
- mount /cache for data persistence

Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-17 23:21:01 +01:00
4587901672 move / check into separat feature ("root")
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-16 00:27:00 +01:00
ce980390df add start time to log
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-16 00:10:50 +01:00
a5d9372806 creating first structure of cinema backend server
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2025-11-16 00:01:14 +01:00
28 changed files with 802 additions and 65 deletions

View File

@@ -0,0 +1,42 @@
name: Build and Push Multi-Arch Docker Image
on:
push:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Gitea Registry
uses: docker/login-action@v2
with:
registry: ${{ secrets.DOMAIN }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Extract Version
working-directory: ./server/cinema
run: |
VERSION=$(grep 'version:' pubspec.yaml | sed 's/version: //')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "MAJOR=$(echo $VERSION | cut -d. -f1)" >> $GITHUB_ENV
echo "MAJOR_MINOR=$(echo $VERSION | cut -d. -f1,2)" >> $GITHUB_ENV
- name: Build and Push Multi-Arch Image
working-directory: ./server/cinema
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ${{ secrets.DOMAIN }}/${{ secrets.OWNER }}/${{ secrets.REPO }}/server:latest \
-t ${{ secrets.DOMAIN }}/${{ secrets.OWNER }}/${{ secrets.REPO }}/server:${{ env.MAJOR }} \
-t ${{ secrets.DOMAIN }}/${{ secrets.OWNER }}/${{ secrets.REPO }}/server:${{ env.MAJOR_MINOR }} \
-t ${{ secrets.DOMAIN }}/${{ secrets.OWNER }}/${{ secrets.REPO }}/server:${{ env.VERSION }} \
--push .

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
**/.idea/libraries
**/.idea/copilot.*
*.log

View File

@@ -20,7 +20,7 @@ body:json {
"backgroundColor": "#000",
"format": "png",
"language": "de-DE",
"output": "lvgl_binary"
"output": "lvglBinary"
}
}

View File

@@ -7,3 +7,6 @@ build/
.gitignore
.idea/
.packages
*.g.dart
*.config.dart
assets/cache

View File

@@ -1,3 +1,8 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
*.g.dart
*.freezed.dart
*.config.dart
*.log
**/cache

View File

@@ -8,14 +8,18 @@ RUN dart pub get
# Copy app source code (except anything in .dockerignore) and AOT compile app.
COPY . .
RUN dart compile exe bin/server.dart -o bin/server
RUN dart run build_runner build --delete-conflicting-outputs && \
APP_VERSION=$(grep 'version:' pubspec.yaml | sed 's/version: //') && \
dart compile exe bin/server.dart -o bin/server \
-DAPP_VERSION=$APP_VERSION
# Build minimal serving image from AOT-compiled `/server`
# and the pre-built AOT-runtime in the `/runtime/` directory of the base image.
FROM scratch
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server /app/bin/
COPY assets /assets
# Start server.
EXPOSE 8080
EXPOSE 3000
CMD ["/app/bin/server"]

26
server/cinema/Makefile Normal file
View File

@@ -0,0 +1,26 @@
VERSION := $(shell grep 'version:' pubspec.yaml | sed 's/version: //')
MAJOR := $(shell echo $(VERSION) | cut -d. -f1)
MAJOR_MINOR := $(shell echo $(VERSION) | cut -d. -f1,2)
build:
dart run build_runner build --delete-conflicting-outputs
watch:
dart run build_runner watch --delete-conflicting-outputs
docker: build
docker build \
-t cr.mars3142.io/model-railway/cinema-display:latest \
-t cr.mars3142.io/model-railway/cinema-display:$(MAJOR) \
-t cr.mars3142.io/model-railway/cinema-display:$(MAJOR_MINOR) \
-t cr.mars3142.io/model-railway/cinema-display:$(VERSION) \
.
multi: build
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t cr.mars3142.io/model-railway/cinema-display:latest \
-t cr.mars3142.io/model-railway/cinema-display:$(MAJOR) \
-t cr.mars3142.io/model-railway/cinema-display:$(MAJOR_MINOR) \
-t cr.mars3142.io/model-railway/cinema-display:$(VERSION) \
.

View File

@@ -0,0 +1,5 @@
____ _ ____ _
/ ___(_)_ __ ___ _ __ ___ __ _ / ___| ___ _ ____ _(_) ___ ___
| | | | '_ \ / _ \ '_ ` _ \ / _` | \___ \ / _ \ '__\ \ / / |/ __/ _ \
| |___| | | | | __/ | | | | | (_| | ___) | __/ | \ V /| | (_| __/
\____|_|_| |_|\___|_| |_| |_|\__,_| |____/ \___|_| \_/ |_|\___\___|

View File

@@ -1,71 +1,62 @@
import 'dart:async';
import 'dart:io';
import 'package:cinema/common/env_not_found_exception.dart';
import 'package:cinema/feature/middlewares/cors.dart';
import 'package:cinema/feature/poster/data/repositories/image_loader.dart';
import 'package:cinema/feature/poster/data/services/poster.service.dart';
import 'package:cinema/feature/root/data/service/root.service.dart';
import 'package:cinema/feature/version/version.dart';
import 'package:cinema/injectable.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart';
final List<dynamic> clients = [];
// Configure routes.
final _router = Router()
..get('/', _rootHandler)
..get('/echo/<message>', _echoHandler);
Response _rootHandler(Request req) {
return Response.ok('Hello, World!\n');
}
Response _echoHandler(Request request) {
final message = request.params['message'];
return Response.ok('$message\n');
}
void main(List<String> args) async {
// Use any available host or container IP (usually `0.0.0.0`).
final ip = InternetAddress.anyIPv4;
runZonedGuarded(
() async {
final startTime = DateTime.now();
// Configure a pipeline that logs requests.
final handler = Pipeline().addMiddleware(logRequests()).addHandler(_router.call);
configureDependencies();
var wsHandler = webSocketHandler((webSocket, _) {
clients.add(webSocket);
print('Client connected, total: ${clients.length}');
final router = Router();
router.mount("/poster", getIt<PosterService>().router.call);
router.mount("/", getIt<RootService>().router.call);
webSocket.stream.listen(
(message) {
webSocket.sink.add('echo $message');
},
onDone: () {
clients.remove(webSocket);
print('Client disconnected, total: ${clients.length}');
},
onError: (error) {
clients.remove(webSocket);
print('Client error: $error');
},
cancelOnError: true,
);
});
FutureOr<Response> combinedHandler(Request request) {
if (request.url.path == 'ws') {
return wsHandler(request);
}
return handler(request);
}
/// add middlewares (Logging, CORS)
final handler = Pipeline().addMiddleware(logRequests()).addMiddleware(cors()).addHandler(router.call);
// For running in containers, we respect the PORT environment variable.
final port = int.parse(Platform.environment['PORT'] ?? '8080');
final server = await shelf_io.serve(combinedHandler, ip, port).then((server) {
print('Serving at http|ws://${server.address.host}:${server.port}');
});
}
// Broadcast to all clients
void broadcast(String message) {
for (final client in clients) {
client.sink.add(message);
final port = int.parse(Platform.environment['PORT'] ?? '3000');
await io.serve(handler, InternetAddress.anyIPv4, port, poweredByHeader: null).then((server) async {
final bannerFile = File('assets/banner.txt');
if (await bannerFile.exists()) {
final banner = await bannerFile.readAsString();
print(banner);
}
print('Caching current trending images...');
final ImageLoader loader = getIt();
final movies = await loader.getPosterURIs();
for (var movie in movies) {
await loader.downloadImages(movie);
}
getIt<Version>().printVersion();
print('Serving at ${server.address.host}:${server.port}\n');
final time = DateTime.now().difference(startTime);
print('Server started at: $startTime');
print('Startup time: $time\n');
});
},
(error, stackTrace) {
stderr.writeln(error);
if (error is EnvNotFoundException) {
exit(1);
}
},
);
}

View File

@@ -0,0 +1,38 @@
import 'dart:io';
import 'package:cinema/common/env_module.dart';
import 'package:cinema/feature/version/version.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
const dioAPI = 'api';
const dioIMAGES = 'images';
@module
abstract class DioModule {
@Named(dioAPI)
@lazySingleton
Dio apiDio(@Named(apiKey) String apiKey) => Dio(
BaseOptions(
baseUrl: 'https://api.themoviedb.org/3',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Cinema Service (v${Version().appVersion})',
},
),
);
@Named(dioIMAGES)
@lazySingleton
Dio get imagesDio => Dio(
BaseOptions(
baseUrl: 'https://image.tmdb.org/t/p/original',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
),
);
}

View File

@@ -0,0 +1,32 @@
import 'package:built_value/serializer.dart';
/// A custom serializer for [DateTime] objects that are represented as
/// a simple date string "yyyy-MM-dd" in JSON.
class CustomDateTimeSerializer implements PrimitiveSerializer<DateTime> {
@override
final Iterable<Type> types = const [DateTime];
@override
final String wireName = 'DateTime';
@override
Object serialize(Serializers serializers, DateTime dateTime,
{FullType specifiedType = FullType.unspecified}) {
// On serialization, convert DateTime to a "yyyy-MM-dd" string.
return dateTime.toIso8601String().substring(0, 10);
}
@override
DateTime deserialize(Serializers serializers, Object serialized,
{FullType specifiedType = FullType.unspecified}) {
// On deserialization, parse the string to a DateTime object.
// This handles formats like "2025-09-23".
final parts = (serialized as String).split('-');
final dateUtc = DateTime.utc(
int.parse(parts[0]),
int.parse(parts[1]),
int.parse(parts[2]),
);
return dateUtc;
}
}

View File

@@ -0,0 +1,20 @@
import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'package:cinema/feature/poster/domain/movie.dart';
import 'package:cinema/feature/poster/domain/tmdb_trending_response.dart';
import 'custom_date_time_serializer.dart';
part 'serializers.g.dart';
@SerializersFor([TmdbTrendingResponse, TmdbMovieResult])
final Serializers serializers =
(_$serializers.toBuilder()
..add(CustomDateTimeSerializer())
..addPlugin(StandardJsonPlugin())
..addBuilderFactory(
const FullType(BuiltList, [FullType(TmdbMovieResult), FullType(Movie)]),
() => ListBuilder<TmdbMovieResult>(),
))
.build();

View File

@@ -0,0 +1,18 @@
import 'dart:io';
import 'package:injectable/injectable.dart';
import 'package:cinema/common/env_not_found_exception.dart';
const apiKey = "apiKey";
@module
abstract class EnvModule {
@lazySingleton
@Named(apiKey)
String get api_key {
final key = Platform.environment['TMDB_API_KEY'];
if (key == null || key.isEmpty) {
throw EnvNotFoundException('TMDB_API_KEY environment variable is missing.');
}
return key;
}
}

View File

@@ -0,0 +1,8 @@
class EnvNotFoundException implements Exception {
final String message;
EnvNotFoundException([this.message = '']);
@override
String toString() => 'EnvNotFoundException: $message';
}

View File

@@ -0,0 +1,24 @@
import 'package:shelf/shelf.dart';
Middleware cors() {
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Origin, Content-Type',
'Access-Control-Max-Age': "600",
};
return createMiddleware(
requestHandler: (Request request) {
if (request.method == "OPTIONS") {
return Response.ok('', headers: corsHeaders);
}
return null;
},
responseHandler: (Response response) {
final headers = Map<String, String>.from(response.headers);
headers.addEntries(corsHeaders.entries.map((e) => MapEntry(e.key, e.value)));
return response.change(headers: headers);
},
);
}

View File

@@ -0,0 +1,7 @@
import 'package:cinema/feature/poster/domain/movie.dart';
abstract class ImageLoader {
Future<List<Movie>> getPosterURIs({String? language = 'de'});
Future<bool> downloadImages(Movie movie);
}

View File

@@ -0,0 +1,60 @@
import 'dart:io';
import 'package:cinema/common/dio_module.dart';
import 'package:cinema/common/domain/serializers.dart';
import 'package:cinema/feature/poster/data/repositories/image_loader.dart';
import 'package:cinema/feature/poster/domain/movie.dart';
import 'package:cinema/feature/poster/domain/tmdb_trending_response.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
@Injectable(as: ImageLoader)
class TmDBImageLoader implements ImageLoader {
final Dio api;
final Dio images;
final String imageBaseUrl = 'https://image.tmdb.org/t/p/w500';
TmDBImageLoader(@Named(dioAPI) this.api, @Named(dioIMAGES) this.images);
@override
Future<List<Movie>> getPosterURIs({String? language = 'de'}) async {
final response = await api.get('/trending/movie/week?language=$language');
if (response.data != null) {
final data = serializers.deserializeWith(TmdbTrendingResponse.serializer, response.data);
return data?.results
.map(
(movie) => Movie(
(b) => b
..id = movie.id
..title = movie.title
..poster = movie.posterPath
..backdrop = movie.backdropPath
..release = movie.releaseDate
..video = movie.video,
),
)
.toList() ??
[];
}
return [];
}
@override
Future<bool> downloadImages(Movie movie) async {
await _downloadImage(movie.poster, "cache/movie/${movie.id}/poster.png");
await _downloadImage(movie.backdrop, "cache/movie/${movie.id}/backdrop.png");
return true;
}
Future<bool> _downloadImage(String url, String filename) async {
final file = File(filename);
if (await file.exists()) {
return false;
}
await file.parent.create(recursive: true);
final response = await images.get(url, options: Options(responseType: ResponseType.bytes));
await file.writeAsBytes(response.data);
return true;
}
}

View File

@@ -0,0 +1,36 @@
import 'dart:convert';
import 'package:cinema/feature/poster/domain/poster_request.schema.dart';
import 'package:injectable/injectable.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
part 'poster.service.g.dart';
@injectable
class PosterService {
@Route.get('/')
Future<Response> getRoot(Request request) async {
return Response.ok('deprecated poster endpoint. use POST /poster instead');
}
@Route.post('/')
Future<Response> postRoot(Request request) async {
final payload = await request.readAsString();
final body = jsonDecode(payload);
final params = posterSchema.safeParse(body);
if (!params.success) {
return Response(
400,
body: jsonEncode({
'error': 'Invalid request',
'details': params.error?.messages,
}),
headers: {'Content-Type': 'application/json'},
);
}
return Response.ok(jsonEncode(params.data), headers: {'Content-Type': 'application/json'});
}
Router get router => _$PosterServiceRouter(this);
}

View File

@@ -0,0 +1,24 @@
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'movie.g.dart';
abstract class Movie implements Built<Movie, MovieBuilder> {
static Serializer<Movie> get serializer => _$movieSerializer;
int get id;
String get title;
DateTime get release;
bool get video;
String get poster;
String get backdrop;
Movie._();
factory Movie([void Function(MovieBuilder) updates]) = _$Movie;
}

View File

@@ -0,0 +1,16 @@
enum PosterOrientation {
horizontal,
vertical,
}
enum PosterFormat {
png,
jpeg,
bmp,
}
enum PosterOutput {
image,
lvgl,
lvglBinary,
}

View File

@@ -0,0 +1,30 @@
import 'package:cinema/feature/poster/domain/poster.enums.dart';
import 'package:zard/zard.dart';
final _orientations = PosterOrientation.values.map((e) => e.name);
final _formats = PosterFormat.values.map((e) => e.name);
final _outputs = PosterOutput.values.map((e) => e.name);
final posterSchema = z.map({
'width': z.int().min(1, message: "'width' must be at least 1").max(4000, message: "'width' must be at most 4000"),
'height': z.int().min(1, message: "'height' must be at least 1").max(4000, message: "'height' must be a most 4000"),
'count': z.int().min(1, message: "'count' must be at least 1").max(20, message: "'count' must be at most 20"),
'orientation': z.string().refine(
(value) => _orientations.contains(value),
message: "'orientation' must be either ${_orientations.join(', ')}",
),
'shuffle': z.bool(message: "'shuffle' must be a boolean value"),
'language': z.string(message: "'language' must be a string").transform((value) => value.trim()),
'backgroundColor': z.string().regex(
RegExp(r'^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$'),
message: "The 'backgroundColor' must be a valid hexadecimal color code (e.g., #000 or #FF0000)",
),
'format': z.string().refine(
(value) => _formats.contains(value),
message: "'format' must be either ${_formats.join(', ')}",
),
'output': z.string().refine(
(value) => _outputs.contains(value),
message: "'output' must be either ${_outputs.join(', ')}",
),
});

View File

@@ -0,0 +1,69 @@
import 'dart:convert';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
part 'tmdb_trending_response.g.dart';
abstract class TmdbTrendingResponse implements Built<TmdbTrendingResponse, TmdbTrendingResponseBuilder> {
static Serializer<TmdbTrendingResponse> get serializer => _$tmdbTrendingResponseSerializer;
int get page;
BuiltList<TmdbMovieResult> get results;
@BuiltValueField(wireName: 'total_pages')
int get totalPages;
@BuiltValueField(wireName: 'total_results')
int get totalResults;
TmdbTrendingResponse._();
factory TmdbTrendingResponse([void Function(TmdbTrendingResponseBuilder) updates]) = _$TmdbTrendingResponse;
}
abstract class TmdbMovieResult implements Built<TmdbMovieResult, TmdbMovieResultBuilder> {
static Serializer<TmdbMovieResult> get serializer => _$tmdbMovieResultSerializer;
bool get adult;
@BuiltValueField(wireName: 'backdrop_path')
String get backdropPath;
int get id;
String get title;
@BuiltValueField(wireName: 'original_title')
String get originalTitle;
String get overview;
@BuiltValueField(wireName: 'poster_path')
String get posterPath;
@BuiltValueField(wireName: 'media_type')
String get mediaType;
@BuiltValueField(wireName: 'genre_ids')
BuiltList<int> get genreIds;
double get popularity;
@BuiltValueField(wireName: 'release_date')
DateTime get releaseDate;
bool get video;
@BuiltValueField(wireName: 'vote_average')
double get voteAverage;
@BuiltValueField(wireName: 'vote_count')
int get voteCount;
TmdbMovieResult._();
factory TmdbMovieResult([void Function(TmdbMovieResultBuilder) updates]) = _$TmdbMovieResult;
}

View File

@@ -0,0 +1,25 @@
import 'package:cinema/feature/websocket/websocket.service.dart';
import 'package:cinema/injectable.dart';
import 'package:injectable/injectable.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
part 'root.service.g.dart';
@injectable
class RootService {
@Route.get('/')
Future<Response> getRoot(Request request) async {
final isWebSocket =
request.headers['connection']?.toLowerCase() == 'upgrade' &&
request.headers['upgrade']?.toLowerCase() == 'websocket';
if (isWebSocket) {
return getIt<WebSocketService>().handler(request);
}
return Response.ok('REST response');
}
Router get router => _$RootServiceRouter(this);
}

View File

@@ -0,0 +1,14 @@
import 'package:injectable/injectable.dart';
@injectable
class Version {
const Version();
String get appVersion => const String.fromEnvironment('APP_VERSION');
void printVersion() {
if (appVersion.isNotEmpty) {
print('App Version: $appVersion');
}
}
}

View File

@@ -0,0 +1,28 @@
import 'package:injectable/injectable.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_web_socket/shelf_web_socket.dart';
@LazySingleton()
class WebSocketService {
final List<dynamic> _clients = [];
Handler get handler => webSocketHandler((webSocket, _) {
_clients.add(webSocket);
print('Client connected, total: ${_clients.length}');
webSocket.stream.listen(
(message) {
webSocket.sink.add('echo $message');
},
onDone: () {
_clients.remove(webSocket);
print('Client disconnected, total: ${_clients.length}');
},
onError: (error) {
_clients.remove(webSocket);
print('Client error: $error');
},
cancelOnError: true,
);
});
}

View File

@@ -0,0 +1,9 @@
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
import 'injectable.config.dart';
final getIt = GetIt.instance;
@InjectableInit()
void configureDependencies() => getIt.init();

View File

@@ -5,18 +5,18 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e"
sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d
url: "https://pub.dev"
source: hosted
version: "92.0.0"
version: "91.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e"
sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08
url: "https://pub.dev"
source: hosted
version: "9.0.0"
version: "8.4.1"
archive:
dependency: transitive
description:
@@ -49,6 +49,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
build:
dependency: transitive
description:
name: build
sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9
url: "https://pub.dev"
source: hosted
version: "4.0.2"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
url: "https://pub.dev"
source: hosted
version: "4.1.1"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "7b5b569f3df370590a85029148d6fc66c7d0201fc6f1847c07dd85d365ae9fcd"
url: "https://pub.dev"
source: hosted
version: "2.10.3"
built_collection:
dependency: "direct main"
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: "direct main"
description:
name: built_value
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d
url: "https://pub.dev"
source: hosted
version: "8.12.0"
built_value_generator:
dependency: "direct dev"
description:
name: built_value_generator
sha256: "65f5823a2c4158384ebc845218e19286fdf5dd04f8ac2cf607b01a502be40b1b"
url: "https://pub.dev"
source: hosted
version: "8.12.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_config:
dependency: transitive
description:
@@ -57,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.0"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243"
url: "https://pub.dev"
source: hosted
version: "4.11.0"
collection:
dependency: transitive
description:
@@ -89,6 +161,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.7"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
url: "https://pub.dev"
source: hosted
version: "3.1.3"
dio:
dependency: "direct main"
description:
name: dio
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
url: "https://pub.dev"
source: hosted
version: "5.9.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
ffi:
dependency: transitive
description:
@@ -105,6 +201,14 @@ packages:
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:
@@ -113,6 +217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
get_it:
dependency: "direct main"
description:
name: get_it
sha256: "84792561b731b6463d053e9761a5236da967c369da10b134b8585a5e18429956"
url: "https://pub.dev"
source: hosted
version: "9.0.5"
glob:
dependency: transitive
description:
@@ -121,6 +233,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.3"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
http:
dependency: "direct dev"
description:
@@ -161,6 +281,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.5.4"
injectable:
dependency: "direct main"
description:
name: injectable
sha256: "29559f7e3daebf0084597de86a825ae7f149d9e30264b7fbc71d1069ae82697d"
url: "https://pub.dev"
source: hosted
version: "2.6.0"
injectable_generator:
dependency: "direct dev"
description:
name: injectable_generator
sha256: "309c3f3546160dd00b575f16b341a6a3025479950441bcc7fcb2f8404a40d326"
url: "https://pub.dev"
source: hosted
version: "2.9.1"
io:
dependency: transitive
description:
@@ -177,6 +313,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
lints:
dependency: "direct dev"
description:
@@ -273,6 +417,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
source: hosted
version: "4.1.0"
shelf:
dependency: "direct main"
description:
@@ -297,6 +457,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.4"
shelf_router_generator:
dependency: "direct dev"
description:
name: shelf_router_generator
sha256: "310416e0eb5a96c8b27f2586367f07b09dc480af06485c1f7951bbed1e8b8b08"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_static:
dependency: transitive
description:
@@ -313,6 +481,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
source_map_stack_trace:
dependency: transitive
description:
@@ -353,6 +529,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@@ -465,5 +649,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.3"
zard:
dependency: "direct main"
description:
name: zard
sha256: "772fc9ef6088123fefaaa88cb986253f0e838aec2af2c3b956a9a1c98ea2b049"
url: "https://pub.dev"
source: hosted
version: "0.0.23"
sdks:
dart: ">=3.9.0 <4.0.0"

View File

@@ -7,12 +7,22 @@ environment:
sdk: ^3.9.0
dependencies:
built_collection: ^5.1.1
built_value: ^8.9.2
dio: ^5.9.0
get_it: ^9.0.5
image: ^4.5.4
injectable: ^2.6.0
shelf: ^1.4.2
shelf_router: ^1.1.2
shelf_web_socket: ^3.0.0
zard: ^0.0.23
dev_dependencies:
build_runner: ^2.10.3
built_value_generator: ^8.9.2
http: ^1.2.2
injectable_generator: ^2.9.1
lints: ^6.0.0
shelf_router_generator: ^1.1.3
test: ^1.25.6