diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 6b9372e..2a98433 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -10,6 +10,14 @@
+## Changed Side
+
+
+
+- [ ] 💻 Backend
+- [ ] 📱 Mobile App
+- [ ] 🔧 Configuration
+
## Type of Change
diff --git a/.github/workflows/build_and_push_backend.yml b/.github/workflows/build_and_push_backend.yml
new file mode 100644
index 0000000..a882279
--- /dev/null
+++ b/.github/workflows/build_and_push_backend.yml
@@ -0,0 +1,60 @@
+name: Build backend and push Docker Image
+
+on:
+ push:
+ branches:
+ - dev
+ paths:
+ - backend/**
+ pull_request:
+ branches:
+ - dev
+ paths:
+ - backend/**
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: dart-lang/setup-dart@v1
+ with:
+ sdk: stable
+
+ - name: Install Dart Frog
+ run: dart pub global activate dart_frog_cli
+
+ - name: Create Dev Build
+ run: dart_frog build
+
+ - name: Change Directory to build folder
+ run: cd build/
+
+ # - name: Overwrite file
+ # uses: "DamianReeves/write-file-action@master"
+ # with:
+ # path: nixpacks.toml
+ # write-mode: overwrite
+ # contents: |
+ # [phases.setup]
+ # nixpkgsArchive = 'bc901a14315f03cb02d5be6d7e4c8075cd0fe36c'
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Build and push
+ uses: docker/build-push-action@v4
+ with:
+ context: build/
+ push: true
+ tags: dungngminh/server:latest
diff --git a/backend/docs/index.html b/backend/docs/index.html
new file mode 100644
index 0000000..d4a6181
--- /dev/null
+++ b/backend/docs/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ SwaggerUI
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/e2e/auth_test.dart b/backend/e2e/auth_test.dart
index 85c97ac..c4a8cca 100644
--- a/backend/e2e/auth_test.dart
+++ b/backend/e2e/auth_test.dart
@@ -91,7 +91,7 @@ void main() {
);
final loginResponse =
- LoginResponse.fromJson(baseResponse.data as Map);
+ LoginResponse.fromJson(baseResponse.result as Map);
expect(response.statusCode, HttpStatus.ok);
expect(
diff --git a/backend/lib/common/error_message_code.dart b/backend/lib/common/error_message_code.dart
new file mode 100644
index 0000000..9da4005
--- /dev/null
+++ b/backend/lib/common/error_message_code.dart
@@ -0,0 +1,18 @@
+class ErrorMessageCode {
+ ErrorMessageCode._();
+
+ // Base
+ static const unknownError = 'unknown-error';
+ static const bodyEmpty = 'body-empty';
+
+ // Login
+ static const notRegisterYet = 'not-register-yet';
+ static const invalidEmailOrPassword = 'invalid-email-or-password';
+
+ // Register
+ static const emailRegisterd = 'email-registerd';
+
+
+ // Blog
+ static const blocNotFound = 'blog-not-found';
+}
diff --git a/backend/lib/dtos/response/auth/login_response.dart b/backend/lib/dtos/response/auth/login_response.dart
index 6ae7a08..4566281 100644
--- a/backend/lib/dtos/response/auth/login_response.dart
+++ b/backend/lib/dtos/response/auth/login_response.dart
@@ -10,7 +10,8 @@ class LoginResponse {
_$LoginResponseFromJson(json);
final String id;
- final String token;
+ final String token;
Map toJson() => _$LoginResponseToJson(this);
}
+
\ No newline at end of file
diff --git a/backend/lib/dtos/response/base_pagination_response.g.dart b/backend/lib/dtos/response/base_pagination_response.g.dart
index 2296c27..05f9361 100644
--- a/backend/lib/dtos/response/base_pagination_response.g.dart
+++ b/backend/lib/dtos/response/base_pagination_response.g.dart
@@ -13,9 +13,11 @@ BasePaginationResponse _$BasePaginationResponseFromJson(
json,
($checkedConvert) {
final val = BasePaginationResponse(
- currentPage: $checkedConvert('current_page', (v) => v as int? ?? 0),
- limit: $checkedConvert('limit', (v) => v as int? ?? 0),
- totalCount: $checkedConvert('total_count', (v) => v as int? ?? 0),
+ currentPage:
+ $checkedConvert('current_page', (v) => (v as num?)?.toInt() ?? 0),
+ limit: $checkedConvert('limit', (v) => (v as num?)?.toInt() ?? 0),
+ totalCount:
+ $checkedConvert('total_count', (v) => (v as num?)?.toInt() ?? 0),
);
return val;
},
diff --git a/backend/lib/dtos/response/base_response_data.dart b/backend/lib/dtos/response/base_response_data.dart
index 46a872b2..cfc1ec9 100644
--- a/backend/lib/dtos/response/base_response_data.dart
+++ b/backend/lib/dtos/response/base_response_data.dart
@@ -13,17 +13,18 @@ part 'base_response_data.g.dart';
class BaseResponseData {
const BaseResponseData({
required this.success,
- this.data,
+ this.result,
+ this.errorCode,
this.message = kSuccessResponseMessage,
});
factory BaseResponseData.fromJson(Map json) =>
_$BaseResponseDataFromJson(json);
- factory BaseResponseData.data(dynamic data) {
+ factory BaseResponseData.data(dynamic result) {
return BaseResponseData(
success: true,
- data: data,
+ result: result,
);
}
@@ -34,28 +35,32 @@ class BaseResponseData {
);
}
- factory BaseResponseData.failed([String? message]) {
+ factory BaseResponseData.failed({String? errorCode, String? message}) {
return BaseResponseData(
success: false,
+ errorCode: errorCode,
message: message ?? kFailedResponseMessage,
);
}
final bool success;
+ final String? errorCode;
final String message;
- final dynamic data;
+ final dynamic result;
Map toJson() => _$BaseResponseDataToJson(this);
BaseResponseData copyWith({
bool? success,
+ String? errorCode,
String? message,
- dynamic data,
+ dynamic result,
}) {
return BaseResponseData(
success: success ?? this.success,
+ errorCode: errorCode ?? this.errorCode,
message: message ?? this.message,
- data: data ?? this.data,
+ result: result ?? this.result,
);
}
}
@@ -79,50 +84,56 @@ class CreatedResponse extends Response {
}
class NotFoundResponse extends Response {
- NotFoundResponse([String? message])
+ NotFoundResponse([String? errorCode, String? message])
: super.json(
statusCode: HttpStatus.notFound,
- body: BaseResponseData.failed(message).toJson(),
+ body: BaseResponseData.failed(errorCode: errorCode, message: message)
+ .toJson(),
);
}
class ConflictResponse extends Response {
- ConflictResponse([String? message])
+ ConflictResponse([String? errorCode, String? message])
: super.json(
statusCode: HttpStatus.conflict,
- body: BaseResponseData.failed(message).toJson(),
+ body: BaseResponseData.failed(errorCode: errorCode, message: message)
+ .toJson(),
);
}
class UnauthorizedResponse extends Response {
- UnauthorizedResponse([String? message])
+ UnauthorizedResponse([String? errorCode, String? message])
: super.json(
statusCode: HttpStatus.unauthorized,
- body: BaseResponseData.failed(message).toJson(),
+ body: BaseResponseData.failed(errorCode: errorCode, message: message)
+ .toJson(),
);
}
class BadRequestResponse extends Response {
- BadRequestResponse([String? message])
+ BadRequestResponse([String? errorCode, String? message])
: super.json(
statusCode: HttpStatus.badRequest,
- body: BaseResponseData.failed(message).toJson(),
+ body: BaseResponseData.failed(errorCode: errorCode, message: message)
+ .toJson(),
);
}
class ForbiddenResponse extends Response {
- ForbiddenResponse([String? message])
+ ForbiddenResponse([String? errorCode, String? message])
: super.json(
statusCode: HttpStatus.forbidden,
- body: BaseResponseData.failed(message).toJson(),
+ body: BaseResponseData.failed(errorCode: errorCode, message: message)
+ .toJson(),
);
}
-class ServerErrorResponse extends Response {
- ServerErrorResponse([String? message])
+class InternalServerErrorResponse extends Response {
+ InternalServerErrorResponse([String? errorCode, String? message])
: super.json(
statusCode: HttpStatus.internalServerError,
- body: BaseResponseData.failed(message).toJson(),
+ body: BaseResponseData.failed(errorCode: errorCode, message: message)
+ .toJson(),
);
}
diff --git a/backend/lib/dtos/response/base_response_data.g.dart b/backend/lib/dtos/response/base_response_data.g.dart
index de59495..3d4297d 100644
--- a/backend/lib/dtos/response/base_response_data.g.dart
+++ b/backend/lib/dtos/response/base_response_data.g.dart
@@ -13,18 +13,19 @@ BaseResponseData _$BaseResponseDataFromJson(Map json) =>
($checkedConvert) {
final val = BaseResponseData(
success: $checkedConvert('success', (v) => v as bool),
- data: $checkedConvert('data', (v) => v),
+ result: $checkedConvert('result', (v) => v),
+ errorCode: $checkedConvert('error_code', (v) => v as String?),
message: $checkedConvert(
'message', (v) => v as String? ?? kSuccessResponseMessage),
);
return val;
},
+ fieldKeyMap: const {'errorCode': 'error_code'},
);
Map _$BaseResponseDataToJson(BaseResponseData instance) {
final val = {
'success': instance.success,
- 'message': instance.message,
};
void writeNotNull(String key, dynamic value) {
@@ -33,6 +34,8 @@ Map _$BaseResponseDataToJson(BaseResponseData instance) {
}
}
- writeNotNull('data', instance.data);
+ writeNotNull('error_code', instance.errorCode);
+ val['message'] = instance.message;
+ writeNotNull('result', instance.result);
return val;
}
diff --git a/backend/lib/dtos/response/blogs/get_blog_response.g.dart b/backend/lib/dtos/response/blogs/get_blog_response.g.dart
index 7281c22..b6cd1a9 100644
--- a/backend/lib/dtos/response/blogs/get_blog_response.g.dart
+++ b/backend/lib/dtos/response/blogs/get_blog_response.g.dart
@@ -69,8 +69,8 @@ UserOfGetBlogResponse _$UserOfGetBlogResponseFromJson(
id: $checkedConvert('id', (v) => v as String),
fullName: $checkedConvert('full_name', (v) => v as String),
email: $checkedConvert('email', (v) => v as String),
- following: $checkedConvert('following', (v) => v as int),
- follower: $checkedConvert('follower', (v) => v as int),
+ following: $checkedConvert('following', (v) => (v as num).toInt()),
+ follower: $checkedConvert('follower', (v) => (v as num).toInt()),
avatarUrl: $checkedConvert('avatar_url', (v) => v as String?),
);
return val;
diff --git a/backend/lib/dtos/response/users/followers/get_user_profile_response.dart b/backend/lib/dtos/response/users/followers/get_user_profile_response.dart
index eb85630..a9da522 100644
--- a/backend/lib/dtos/response/users/followers/get_user_profile_response.dart
+++ b/backend/lib/dtos/response/users/followers/get_user_profile_response.dart
@@ -1,5 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:very_good_blog_app_backend/models/following_follower.dart';
+import 'package:very_good_blog_app_backend/models/user.dart';
part 'get_user_profile_response.g.dart';
@@ -15,12 +16,12 @@ class GetUserFollowerResponse {
factory GetUserFollowerResponse.fromJson(Map json) =>
_$GetUserFollowerResponseFromJson(json);
- factory GetUserFollowerResponse.fromView(FollowingFollowerView view) {
+ factory GetUserFollowerResponse.fromView(UserView follower) {
return GetUserFollowerResponse(
- id: view.follower.id,
- fullName: view.follower.fullName,
- email: view.follower.email,
- avatarUrl: view.follower.avatarUrl,
+ id: follower.id,
+ fullName: follower.fullName,
+ email: follower.email,
+ avatarUrl: follower.avatarUrl,
);
}
diff --git a/backend/lib/dtos/response/users/followings/get_user_following_response.dart b/backend/lib/dtos/response/users/followings/get_user_following_response.dart
index 00518e3..7d55594 100644
--- a/backend/lib/dtos/response/users/followings/get_user_following_response.dart
+++ b/backend/lib/dtos/response/users/followings/get_user_following_response.dart
@@ -1,5 +1,5 @@
import 'package:json_annotation/json_annotation.dart';
-import 'package:very_good_blog_app_backend/models/following_follower.dart';
+import 'package:very_good_blog_app_backend/models/user.dart';
part 'get_user_following_response.g.dart';
@@ -15,12 +15,12 @@ class GetUserFollowingResponse {
factory GetUserFollowingResponse.fromJson(Map json) =>
_$GetUserFollowingResponseFromJson(json);
- factory GetUserFollowingResponse.fromView(FollowingFollowerView view) {
+ factory GetUserFollowingResponse.fromView(UserView following) {
return GetUserFollowingResponse(
- id: view.following.id,
- fullName: view.following.fullName,
- email: view.following.email,
- avatarUrl: view.following.avatarUrl,
+ id: following.id,
+ fullName: following.fullName,
+ email: following.email,
+ avatarUrl: following.avatarUrl,
);
}
diff --git a/backend/lib/dtos/response/users/profiles/get_user_profile_response.g.dart b/backend/lib/dtos/response/users/profiles/get_user_profile_response.g.dart
index b4a7b62..7938563 100644
--- a/backend/lib/dtos/response/users/profiles/get_user_profile_response.g.dart
+++ b/backend/lib/dtos/response/users/profiles/get_user_profile_response.g.dart
@@ -16,8 +16,8 @@ GetUserProfileResponse _$GetUserProfileResponseFromJson(
id: $checkedConvert('id', (v) => v as String),
fullName: $checkedConvert('full_name', (v) => v as String),
email: $checkedConvert('email', (v) => v as String),
- following: $checkedConvert('following', (v) => v as int),
- follower: $checkedConvert('follower', (v) => v as int),
+ following: $checkedConvert('following', (v) => (v as num).toInt()),
+ follower: $checkedConvert('follower', (v) => (v as num).toInt()),
avatarUrl: $checkedConvert('avatar_url', (v) => v as String?),
);
return val;
diff --git a/backend/lib/models/blog.schema.dart b/backend/lib/models/blog.schema.dart
index 905513c..dd0daa2 100644
--- a/backend/lib/models/blog.schema.dart
+++ b/backend/lib/models/blog.schema.dart
@@ -2,7 +2,7 @@
part of 'blog.dart';
-extension BlogRepositories on Database {
+extension BlogRepositories on Session {
BlogRepository get blogs => BlogRepository._(this);
}
@@ -12,7 +12,7 @@ abstract class BlogRepository
ModelRepositoryInsert,
ModelRepositoryUpdate,
ModelRepositoryDelete {
- factory BlogRepository._(Database db) = _BlogRepository;
+ factory BlogRepository._(Session db) = _BlogRepository;
Future queryBlog(String id);
Future> queryBlogs([QueryParams? params]);
@@ -40,17 +40,18 @@ class _BlogRepository extends BaseRepository
Future insert(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'INSERT INTO "blogs" ( "id", "title", "content", "image_url", "category", "created_at", "updated_at", "creator_id", "is_deleted" )\n'
- 'VALUES ${requests.map((r) => '( ${values.add(r.id)}:text, ${values.add(r.title)}:text, ${values.add(r.content)}:text, ${values.add(r.imageUrl)}:text, ${values.add(EnumTypeConverter([
- BlogCategory.business,
- BlogCategory.technology,
- BlogCategory.fashion,
- BlogCategory.travel,
- BlogCategory.food,
- BlogCategory.education
- ]).tryEncode(r.category))}:text, ${values.add(r.createdAt)}:timestamp, ${values.add(r.updatedAt)}:timestamp, ${values.add(r.creatorId)}:text, ${values.add(r.isDeleted)}:boolean )').join(', ')}\n',
- values.values,
+ await db.execute(
+ Sql.named(
+ 'INSERT INTO "blogs" ( "id", "title", "content", "image_url", "category", "created_at", "updated_at", "creator_id", "is_deleted" )\n'
+ 'VALUES ${requests.map((r) => '( ${values.add(r.id)}:text, ${values.add(r.title)}:text, ${values.add(r.content)}:text, ${values.add(r.imageUrl)}:text, ${values.add(EnumTypeConverter([
+ BlogCategory.business,
+ BlogCategory.technology,
+ BlogCategory.fashion,
+ BlogCategory.travel,
+ BlogCategory.food,
+ BlogCategory.education
+ ]).tryEncode(r.category))}:text, ${values.add(r.createdAt)}:timestamp, ${values.add(r.updatedAt)}:timestamp, ${values.add(r.creatorId)}:text, ${values.add(r.isDeleted)}:boolean )').join(', ')}\n'),
+ parameters: values.values,
);
}
@@ -58,20 +59,20 @@ class _BlogRepository extends BaseRepository
Future update(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'UPDATE "blogs"\n'
- 'SET "title" = COALESCE(UPDATED."title", "blogs"."title"), "content" = COALESCE(UPDATED."content", "blogs"."content"), "image_url" = COALESCE(UPDATED."image_url", "blogs"."image_url"), "category" = COALESCE(UPDATED."category", "blogs"."category"), "created_at" = COALESCE(UPDATED."created_at", "blogs"."created_at"), "updated_at" = COALESCE(UPDATED."updated_at", "blogs"."updated_at"), "creator_id" = COALESCE(UPDATED."creator_id", "blogs"."creator_id"), "is_deleted" = COALESCE(UPDATED."is_deleted", "blogs"."is_deleted")\n'
- 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}:text::text, ${values.add(r.title)}:text::text, ${values.add(r.content)}:text::text, ${values.add(r.imageUrl)}:text::text, ${values.add(EnumTypeConverter([
- BlogCategory.business,
- BlogCategory.technology,
- BlogCategory.fashion,
- BlogCategory.travel,
- BlogCategory.food,
- BlogCategory.education
- ]).tryEncode(r.category))}:text::text, ${values.add(r.createdAt)}:timestamp::timestamp, ${values.add(r.updatedAt)}:timestamp::timestamp, ${values.add(r.creatorId)}:text::text, ${values.add(r.isDeleted)}:boolean::boolean )').join(', ')} )\n'
- 'AS UPDATED("id", "title", "content", "image_url", "category", "created_at", "updated_at", "creator_id", "is_deleted")\n'
- 'WHERE "blogs"."id" = UPDATED."id"',
- values.values,
+ await db.execute(
+ Sql.named('UPDATE "blogs"\n'
+ 'SET "title" = COALESCE(UPDATED."title", "blogs"."title"), "content" = COALESCE(UPDATED."content", "blogs"."content"), "image_url" = COALESCE(UPDATED."image_url", "blogs"."image_url"), "category" = COALESCE(UPDATED."category", "blogs"."category"), "created_at" = COALESCE(UPDATED."created_at", "blogs"."created_at"), "updated_at" = COALESCE(UPDATED."updated_at", "blogs"."updated_at"), "creator_id" = COALESCE(UPDATED."creator_id", "blogs"."creator_id"), "is_deleted" = COALESCE(UPDATED."is_deleted", "blogs"."is_deleted")\n'
+ 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}:text::text, ${values.add(r.title)}:text::text, ${values.add(r.content)}:text::text, ${values.add(r.imageUrl)}:text::text, ${values.add(EnumTypeConverter([
+ BlogCategory.business,
+ BlogCategory.technology,
+ BlogCategory.fashion,
+ BlogCategory.travel,
+ BlogCategory.food,
+ BlogCategory.education
+ ]).tryEncode(r.category))}:text::text, ${values.add(r.createdAt)}:timestamp::timestamp, ${values.add(r.updatedAt)}:timestamp::timestamp, ${values.add(r.creatorId)}:text::text, ${values.add(r.isDeleted)}:boolean::boolean )').join(', ')} )\n'
+ 'AS UPDATED("id", "title", "content", "image_url", "category", "created_at", "updated_at", "creator_id", "is_deleted")\n'
+ 'WHERE "blogs"."id" = UPDATED."id"'),
+ parameters: values.values,
);
}
}
diff --git a/backend/lib/models/favorite_blogs_users.schema.dart b/backend/lib/models/favorite_blogs_users.schema.dart
index 88a9343..6fb0977 100644
--- a/backend/lib/models/favorite_blogs_users.schema.dart
+++ b/backend/lib/models/favorite_blogs_users.schema.dart
@@ -2,7 +2,7 @@
part of 'favorite_blogs_users.dart';
-extension FavoriteBlogsUsersRepositories on Database {
+extension FavoriteBlogsUsersRepositories on Session {
FavoriteBlogsUsersRepository get favoriteBlogsUserses => FavoriteBlogsUsersRepository._(this);
}
@@ -11,7 +11,7 @@ abstract class FavoriteBlogsUsersRepository
ModelRepository,
ModelRepositoryInsert,
ModelRepositoryUpdate {
- factory FavoriteBlogsUsersRepository._(Database db) = _FavoriteBlogsUsersRepository;
+ factory FavoriteBlogsUsersRepository._(Session db) = _FavoriteBlogsUsersRepository;
Future> queryFavoriteBlogsUserses([QueryParams? params]);
}
@@ -32,10 +32,10 @@ class _FavoriteBlogsUsersRepository extends BaseRepository
Future insert(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'INSERT INTO "favorite_blogs_userses" ( "blog_id", "user_id" )\n'
- 'VALUES ${requests.map((r) => '( ${values.add(r.blogId)}:text, ${values.add(r.userId)}:text )').join(', ')}\n',
- values.values,
+ await db.execute(
+ Sql.named('INSERT INTO "favorite_blogs_userses" ( "blog_id", "user_id" )\n'
+ 'VALUES ${requests.map((r) => '( ${values.add(r.blogId)}:text, ${values.add(r.userId)}:text )').join(', ')}\n'),
+ parameters: values.values,
);
}
@@ -43,13 +43,13 @@ class _FavoriteBlogsUsersRepository extends BaseRepository
Future update(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'UPDATE "favorite_blogs_userses"\n'
- 'SET \n'
- 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.blogId)}:text::text, ${values.add(r.userId)}:text::text )').join(', ')} )\n'
- 'AS UPDATED("blog_id", "user_id")\n'
- 'WHERE "favorite_blogs_userses"."blog_id" = UPDATED."blog_id" AND "favorite_blogs_userses"."user_id" = UPDATED."user_id"',
- values.values,
+ await db.execute(
+ Sql.named('UPDATE "favorite_blogs_userses"\n'
+ 'SET \n'
+ 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.blogId)}:text::text, ${values.add(r.userId)}:text::text )').join(', ')} )\n'
+ 'AS UPDATED("blog_id", "user_id")\n'
+ 'WHERE "favorite_blogs_userses"."blog_id" = UPDATED."blog_id" AND "favorite_blogs_userses"."user_id" = UPDATED."user_id"'),
+ parameters: values.values,
);
}
}
diff --git a/backend/lib/models/following_follower.dart b/backend/lib/models/following_follower.dart
index 7ce4f0e..60b2b66 100644
--- a/backend/lib/models/following_follower.dart
+++ b/backend/lib/models/following_follower.dart
@@ -1,10 +1,9 @@
import 'package:stormberry/stormberry.dart';
-import 'package:very_good_blog_app_backend/models/user.dart';
part 'following_follower.schema.dart';
@Model()
abstract class FollowingFollower {
- User get following;
- User get follower;
+ String get followingId;
+ String get followerId;
}
diff --git a/backend/lib/models/following_follower.schema.dart b/backend/lib/models/following_follower.schema.dart
index b268b3b..b6bdbc0 100644
--- a/backend/lib/models/following_follower.schema.dart
+++ b/backend/lib/models/following_follower.schema.dart
@@ -2,7 +2,7 @@
part of 'following_follower.dart';
-extension FollowingFollowerRepositories on Database {
+extension FollowingFollowerRepositories on Session {
FollowingFollowerRepository get followingFollowers => FollowingFollowerRepository._(this);
}
@@ -11,7 +11,7 @@ abstract class FollowingFollowerRepository
ModelRepository,
ModelRepositoryInsert,
ModelRepositoryUpdate {
- factory FollowingFollowerRepository._(Database db) = _FollowingFollowerRepository;
+ factory FollowingFollowerRepository._(Session db) = _FollowingFollowerRepository;
Future> queryFollowingFollowers([QueryParams? params]);
}
@@ -32,10 +32,10 @@ class _FollowingFollowerRepository extends BaseRepository
Future insert(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'INSERT INTO "following_followers" ( "following_id", "follower_id" )\n'
- 'VALUES ${requests.map((r) => '( ${values.add(r.followingId)}:text, ${values.add(r.followerId)}:text )').join(', ')}\n',
- values.values,
+ await db.execute(
+ Sql.named('INSERT INTO "following_followers" ( "following_id", "follower_id" )\n'
+ 'VALUES ${requests.map((r) => '( ${values.add(r.followingId)}:text, ${values.add(r.followerId)}:text )').join(', ')}\n'),
+ parameters: values.values,
);
}
@@ -43,13 +43,13 @@ class _FollowingFollowerRepository extends BaseRepository
Future update(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'UPDATE "following_followers"\n'
- 'SET \n'
- 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.followingId)}:text::text, ${values.add(r.followerId)}:text::text )').join(', ')} )\n'
- 'AS UPDATED("following_id", "follower_id")\n'
- 'WHERE "following_followers"."following_id" = UPDATED."following_id" AND "following_followers"."follower_id" = UPDATED."follower_id"',
- values.values,
+ await db.execute(
+ Sql.named('UPDATE "following_followers"\n'
+ 'SET "following_id" = COALESCE(UPDATED."following_id", "following_followers"."following_id"), "follower_id" = COALESCE(UPDATED."follower_id", "following_followers"."follower_id")\n'
+ 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.followingId)}:text::text, ${values.add(r.followerId)}:text::text )').join(', ')} )\n'
+ 'AS UPDATED("following_id", "follower_id")\n'
+ 'WHERE '),
+ parameters: values.values,
);
}
}
@@ -76,29 +76,23 @@ class FollowingFollowerUpdateRequest {
class FollowingFollowerViewQueryable extends ViewQueryable {
@override
- String get query =>
- 'SELECT "following_followers".*, row_to_json("following".*) as "following", row_to_json("follower".*) as "follower"'
- 'FROM "following_followers"'
- 'LEFT JOIN (${UserViewQueryable().query}) "following"'
- 'ON "following_followers"."following_id" = "following"."id"'
- 'LEFT JOIN (${UserViewQueryable().query}) "follower"'
- 'ON "following_followers"."follower_id" = "follower"."id"';
+ String get query => 'SELECT "following_followers".*'
+ 'FROM "following_followers"';
@override
String get tableAlias => 'following_followers';
@override
FollowingFollowerView decode(TypedMap map) => FollowingFollowerView(
- following: map.get('following', UserViewQueryable().decoder),
- follower: map.get('follower', UserViewQueryable().decoder));
+ followingId: map.get('following_id'), followerId: map.get('follower_id'));
}
class FollowingFollowerView {
FollowingFollowerView({
- required this.following,
- required this.follower,
+ required this.followingId,
+ required this.followerId,
});
- final UserView following;
- final UserView follower;
+ final String followingId;
+ final String followerId;
}
diff --git a/backend/lib/models/user.schema.dart b/backend/lib/models/user.schema.dart
index c6df0ea..119a331 100644
--- a/backend/lib/models/user.schema.dart
+++ b/backend/lib/models/user.schema.dart
@@ -2,7 +2,7 @@
part of 'user.dart';
-extension UserRepositories on Database {
+extension UserRepositories on Session {
UserRepository get users => UserRepository._(this);
}
@@ -12,7 +12,7 @@ abstract class UserRepository
ModelRepositoryInsert,
ModelRepositoryUpdate,
ModelRepositoryDelete {
- factory UserRepository._(Database db) = _UserRepository;
+ factory UserRepository._(Session db) = _UserRepository;
Future queryUser(String id);
Future> queryUsers([QueryParams? params]);
@@ -40,10 +40,11 @@ class _UserRepository extends BaseRepository
Future insert(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'INSERT INTO "users" ( "id", "full_name", "email", "password", "avatar_url", "following", "follower" )\n'
- 'VALUES ${requests.map((r) => '( ${values.add(r.id)}:text, ${values.add(r.fullName)}:text, ${values.add(r.email)}:text, ${values.add(r.password)}:text, ${values.add(r.avatarUrl)}:text, ${values.add(r.following)}:int8, ${values.add(r.follower)}:int8 )').join(', ')}\n',
- values.values,
+ await db.execute(
+ Sql.named(
+ 'INSERT INTO "users" ( "id", "full_name", "email", "password", "avatar_url", "following", "follower" )\n'
+ 'VALUES ${requests.map((r) => '( ${values.add(r.id)}:text, ${values.add(r.fullName)}:text, ${values.add(r.email)}:text, ${values.add(r.password)}:text, ${values.add(r.avatarUrl)}:text, ${values.add(r.following)}:int8, ${values.add(r.follower)}:int8 )').join(', ')}\n'),
+ parameters: values.values,
);
}
@@ -51,13 +52,13 @@ class _UserRepository extends BaseRepository
Future update(List requests) async {
if (requests.isEmpty) return;
var values = QueryValues();
- await db.query(
- 'UPDATE "users"\n'
- 'SET "full_name" = COALESCE(UPDATED."full_name", "users"."full_name"), "email" = COALESCE(UPDATED."email", "users"."email"), "password" = COALESCE(UPDATED."password", "users"."password"), "avatar_url" = COALESCE(UPDATED."avatar_url", "users"."avatar_url"), "following" = COALESCE(UPDATED."following", "users"."following"), "follower" = COALESCE(UPDATED."follower", "users"."follower")\n'
- 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}:text::text, ${values.add(r.fullName)}:text::text, ${values.add(r.email)}:text::text, ${values.add(r.password)}:text::text, ${values.add(r.avatarUrl)}:text::text, ${values.add(r.following)}:int8::int8, ${values.add(r.follower)}:int8::int8 )').join(', ')} )\n'
- 'AS UPDATED("id", "full_name", "email", "password", "avatar_url", "following", "follower")\n'
- 'WHERE "users"."id" = UPDATED."id"',
- values.values,
+ await db.execute(
+ Sql.named('UPDATE "users"\n'
+ 'SET "full_name" = COALESCE(UPDATED."full_name", "users"."full_name"), "email" = COALESCE(UPDATED."email", "users"."email"), "password" = COALESCE(UPDATED."password", "users"."password"), "avatar_url" = COALESCE(UPDATED."avatar_url", "users"."avatar_url"), "following" = COALESCE(UPDATED."following", "users"."following"), "follower" = COALESCE(UPDATED."follower", "users"."follower")\n'
+ 'FROM ( VALUES ${requests.map((r) => '( ${values.add(r.id)}:text::text, ${values.add(r.fullName)}:text::text, ${values.add(r.email)}:text::text, ${values.add(r.password)}:text::text, ${values.add(r.avatarUrl)}:text::text, ${values.add(r.following)}:int8::int8, ${values.add(r.follower)}:int8::int8 )').join(', ')} )\n'
+ 'AS UPDATED("id", "full_name", "email", "password", "avatar_url", "following", "follower")\n'
+ 'WHERE "users"."id" = UPDATED."id"'),
+ parameters: values.values,
);
}
}
diff --git a/backend/main.dart b/backend/main.dart
index fc9aa77..6569281 100644
--- a/backend/main.dart
+++ b/backend/main.dart
@@ -3,5 +3,13 @@ import 'dart:io';
import 'package:dart_frog/dart_frog.dart';
Future run(Handler handler, InternetAddress ip, int port) {
- return serve(handler, ip, port, poweredByHeader: null);
+ final flavor = Platform.environment['FLAVOR'];
+ if (flavor == 'DEV') {
+ return serve(handler, ip, port, poweredByHeader: null);
+ }
+ const customStaticDocumentPath = 'docs';
+ final cascade = Cascade()
+ .add(createStaticFileHandler(path: customStaticDocumentPath))
+ .add(handler);
+ return serve(cascade.handler, ip, port, poweredByHeader: null);
}
diff --git a/backend/public/openapi.json b/backend/public/openapi.json
index 27c189f..635cba0 100644
--- a/backend/public/openapi.json
+++ b/backend/public/openapi.json
@@ -1,19 +1,12 @@
{
"openapi": "3.0.3",
"info": {
- "title": "A sample API",
- "description": "A sample API",
- "termsOfService": "https://very-good-blog-app.up.railway.app",
+ "title": "Very Good Blog App Dart API Document",
"contact": {
- "name": "none",
- "url": "http://localhost",
- "email": "none@api.com"
+ "name": "dungngminh",
+ "email": "ngminhdung1311@gmail.com"
},
- "license": {
- "name": "",
- "url": ""
- },
- "version": "0.0.0"
+ "version": "1.0.0"
},
"externalDocs": {
"description": "",
@@ -159,6 +152,16 @@
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "search",
+ "in": "query",
+ "required": false,
+ "deprecated": false,
+ "allowEmptyValue": false,
+ "schema": {
+ "type": "string"
+ }
}
],
"security": []
@@ -205,6 +208,16 @@
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "search",
+ "in": "query",
+ "required": false,
+ "deprecated": false,
+ "allowEmptyValue": false,
+ "schema": {
+ "type": "string"
+ }
}
],
"security": []
diff --git a/backend/pubspec.yaml b/backend/pubspec.yaml
index a1ed922..8f281f9 100644
--- a/backend/pubspec.yaml
+++ b/backend/pubspec.yaml
@@ -4,26 +4,26 @@ version: 1.0.0+1
publish_to: none
environment:
- sdk: ">=3.0.0 <4.0.0"
+ sdk: ">=3.3.0 <4.0.0"
dependencies:
cloudinary: ^1.2.0
crypto: ^3.0.3
- dart_frog: ^1.0.0
+ dart_frog: ^1.1.0
dart_frog_auth: ^1.1.0
- dart_jsonwebtoken: ^2.11.0
+ dart_jsonwebtoken: ^2.14.0
dartx: ^1.2.0
equatable: ^2.0.5
- http: ^1.1.0
- json_annotation: ^4.8.1
+ http: ^1.2.1
+ json_annotation: ^4.9.0
shelf_enforces_ssl: ^1.2.1
- stormberry: ^0.13.1
- string_validator: ^1.0.0
- uuid: ^3.0.7
+ stormberry: ^0.14.0
+ string_validator: ^1.0.2
+ uuid: ^4.4.0
dev_dependencies:
- build_runner: ^2.4.6
- json_serializable: ^6.7.1
- mocktail: ^0.3.0
- test: ^1.19.2
- very_good_analysis: ^5.0.0
+ build_runner: ^2.4.9
+ json_serializable: ^6.8.0
+ mocktail: ^1.0.3
+ test: ^1.25.4
+ very_good_analysis: ^5.1.0
diff --git a/backend/routes/_middleware.dart b/backend/routes/_middleware.dart
index c6a0711..07affdb 100644
--- a/backend/routes/_middleware.dart
+++ b/backend/routes/_middleware.dart
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:cloudinary/cloudinary.dart';
import 'package:dart_frog/dart_frog.dart';
+import 'package:dartx/dartx.dart';
import 'package:stormberry/stormberry.dart';
import 'package:very_good_blog_app_backend/models/user.dart';
import 'package:very_good_blog_app_backend/util/jwt_handler.dart';
@@ -10,15 +11,15 @@ final db = Database(
host: Platform.environment['PGHOST'],
port: int.tryParse(Platform.environment['PGPORT'] ?? '5432'),
database: Platform.environment['PGDATABASE'],
- user: Platform.environment['PGUSER'],
+ username: Platform.environment['PGUSER'],
password: Platform.environment['PGPASSWORD'],
useSSL: false,
);
final cloudinary = Cloudinary.signedConfig(
- apiKey: Platform.environment['CLOUDINARY_APIKEY'] ?? '',
- apiSecret: Platform.environment['CLOUDINARY_APISECRET'] ?? '',
- cloudName: Platform.environment['CLOUDINARY_CLOUDNAME'] ?? '',
+ apiKey: Platform.environment['CLOUDINARY_APIKEY'].orEmpty(),
+ apiSecret: Platform.environment['CLOUDINARY_APISECRET'].orEmpty(),
+ cloudName: Platform.environment['CLOUDINARY_CLOUDNAME'].orEmpty(),
);
Handler middleware(Handler handler) {
diff --git a/backend/routes/api/auth/login/index.dart b/backend/routes/api/auth/login/index.dart
index d067b1b..f4570d3 100644
--- a/backend/routes/api/auth/login/index.dart
+++ b/backend/routes/api/auth/login/index.dart
@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:dart_frog/dart_frog.dart';
import 'package:stormberry/stormberry.dart';
-import 'package:string_validator/string_validator.dart';
+import 'package:very_good_blog_app_backend/common/error_message_code.dart';
import 'package:very_good_blog_app_backend/common/extensions/hash_extension.dart';
import 'package:very_good_blog_app_backend/common/extensions/json_ext.dart';
import 'package:very_good_blog_app_backend/dtos/request/auth/login_request.dart';
@@ -24,13 +24,9 @@ Future _onLoginPostRequest(RequestContext context) async {
final body = await context.request.body();
- if (body.isEmpty) return BadRequestResponse();
+ if (body.isEmpty) return BadRequestResponse(ErrorMessageCode.bodyEmpty);
final request = LoginRequest.fromJson(body.asJson());
- if (!isEmail(request.email)) {
- return BadRequestResponse('Email format is wrong, please check again');
- }
-
return db.users
.queryUsers(
QueryParams(
@@ -41,9 +37,10 @@ Future _onLoginPostRequest(RequestContext context) async {
.then((users) {
final user = users.firstOrNull;
return user == null
- ? BadRequestResponse('User is not registered')
+ ? BadRequestResponse(ErrorMessageCode.notRegisterYet)
: OkResponse(
LoginResponse(id: user.id, token: createJwt(user.id)).toJson(),
);
- }).onError((e, _) => ServerErrorResponse(e.toString()));
+ }).onError(
+ (e, _) => InternalServerErrorResponse(ErrorMessageCode.unknownError));
}
diff --git a/backend/routes/api/auth/register/index.dart b/backend/routes/api/auth/register/index.dart
index dd82c9c..6900d54 100644
--- a/backend/routes/api/auth/register/index.dart
+++ b/backend/routes/api/auth/register/index.dart
@@ -2,8 +2,8 @@ import 'dart:async';
import 'package:dart_frog/dart_frog.dart';
import 'package:stormberry/stormberry.dart';
-import 'package:string_validator/string_validator.dart';
import 'package:uuid/uuid.dart';
+import 'package:very_good_blog_app_backend/common/error_message_code.dart';
import 'package:very_good_blog_app_backend/common/extensions/hash_extension.dart';
import 'package:very_good_blog_app_backend/common/extensions/json_ext.dart';
import 'package:very_good_blog_app_backend/dtos/request/auth/register_request.dart';
@@ -23,18 +23,10 @@ Future _onRegisterPostRequest(RequestContext context) async {
final body = await context.request.body();
- if (body.isEmpty) return BadRequestResponse();
+ if (body.isEmpty) return BadRequestResponse(ErrorMessageCode.bodyEmpty);
final request = RegisterRequest.fromJson(body.asJson());
- if (request.password != request.confirmationPassword) {
- return BadRequestResponse('Confirmation password not match');
- }
-
- if (!isEmail(request.email)) {
- return BadRequestResponse('Email format is wrong, please check again');
- }
-
final users = await db.users.queryUsers(
QueryParams(
where: 'email=@email',
@@ -44,20 +36,22 @@ Future _onRegisterPostRequest(RequestContext context) async {
),
);
if (users.isNotEmpty) {
- return ConflictResponse('This email was registered');
+ return ConflictResponse(ErrorMessageCode.emailRegisterd);
}
- return db.users
- .insertOne(
- UserInsertRequest(
- email: request.email,
- follower: 0,
- following: 0,
- fullName: request.fullName,
- id: const Uuid().v4(),
- password: request.password.hashValue,
- ),
- )
+ return db.users
+ .insertOne(
+ UserInsertRequest(
+ email: request.email,
+ follower: 0,
+ following: 0,
+ fullName: request.fullName,
+ id: const Uuid().v4(),
+ password: request.password.hashValue,
+ ),
+ )
.then((_) => CreatedResponse())
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError(
+ (e, st) => InternalServerErrorResponse(ErrorMessageCode.unknownError),
+ );
}
diff --git a/backend/routes/api/blogs/[id]/index.dart b/backend/routes/api/blogs/[id]/index.dart
index c89fe54..34146b0 100644
--- a/backend/routes/api/blogs/[id]/index.dart
+++ b/backend/routes/api/blogs/[id]/index.dart
@@ -1,6 +1,7 @@
import 'package:dart_frog/dart_frog.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:stormberry/stormberry.dart';
+import 'package:very_good_blog_app_backend/common/error_message_code.dart';
import 'package:very_good_blog_app_backend/common/extensions/header_extesion.dart';
import 'package:very_good_blog_app_backend/common/extensions/json_ext.dart';
import 'package:very_good_blog_app_backend/dtos/request/blogs/edit_blog_request.dart';
@@ -52,7 +53,7 @@ Future _onBlogsGetRequest(RequestContext context, String id) async {
.toJson(),
);
} catch (e) {
- return ServerErrorResponse(e.toString());
+ return InternalServerErrorResponse(e.toString());
}
}
@@ -61,7 +62,7 @@ Future _onBlogsPatchRequest(RequestContext context, String id) async {
final user = context.read();
try {
final body = await context.request.body();
- if (body.isEmpty) return BadRequestResponse();
+ if (body.isEmpty) return BadRequestResponse(ErrorMessageCode.bodyEmpty);
final request = EditBlogRequest.fromJson(body.asJson());
final blog = await db.blogs.queryBlog(id);
if (blog == null) return NotFoundResponse('Blog not found');
@@ -82,7 +83,7 @@ Future _onBlogsPatchRequest(RequestContext context, String id) async {
} on CheckedFromJsonException catch (e) {
return BadRequestResponse(e.message);
} catch (e) {
- return ServerErrorResponse(e.toString());
+ return InternalServerErrorResponse(e.toString());
}
}
@@ -101,6 +102,6 @@ Future _onBlogsDeleteRequest(
await db.blogs.deleteOne(id);
return OkResponse();
} catch (e) {
- return ServerErrorResponse(e.toString());
+ return InternalServerErrorResponse(e.toString());
}
}
diff --git a/backend/routes/api/blogs/index.dart b/backend/routes/api/blogs/index.dart
index 58cd7b0..76dcd55 100644
--- a/backend/routes/api/blogs/index.dart
+++ b/backend/routes/api/blogs/index.dart
@@ -1,10 +1,10 @@
import 'package:dart_frog/dart_frog.dart';
+import 'package:dartx/dartx.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:stormberry/stormberry.dart';
import 'package:uuid/uuid.dart';
import 'package:very_good_blog_app_backend/common/extensions/json_ext.dart';
import 'package:very_good_blog_app_backend/dtos/request/blogs/create_blog_request.dart';
-import 'package:very_good_blog_app_backend/dtos/response/base_pagination_response.dart';
import 'package:very_good_blog_app_backend/dtos/response/base_response_data.dart';
import 'package:very_good_blog_app_backend/dtos/response/blogs/get_blog_response.dart';
import 'package:very_good_blog_app_backend/models/blog.dart';
@@ -13,6 +13,7 @@ import 'package:very_good_blog_app_backend/models/user.dart';
/// @Allow(GET, POST)
/// @Query(limit)
/// @Query(page)
+/// @Query(search)
Future onRequest(RequestContext context) {
return switch (context.request.method) {
HttpMethod.get => _onBlogsGetRequest(context),
@@ -24,8 +25,8 @@ Future onRequest(RequestContext context) {
Future _onBlogsGetRequest(RequestContext context) async {
final db = context.read();
final queryParams = context.request.uri.queryParameters;
- final limit = int.tryParse(queryParams['limit'] ?? '') ?? 20;
- final currentPage = int.tryParse(queryParams['page'] ?? '') ?? 1;
+ final limit = int.tryParse(queryParams['limit'].orEmpty()) ?? 20;
+ final currentPage = int.tryParse(queryParams['page'].orEmpty()) ?? 1;
try {
final results = await db.blogs.queryBlogs(
@@ -35,19 +36,9 @@ Future _onBlogsGetRequest(RequestContext context) async {
),
);
final blogs = results.map(GetBlogResponse.fromView);
- final pagination = BasePaginationResponse(
- currentPage: currentPage,
- limit: limit,
- totalCount: blogs.length,
- );
- return OkResponse(
- {
- 'blogs': blogs.map((e) => e.toJson()).toList(),
- 'pagination': pagination.toJson(),
- },
- );
+ return OkResponse(blogs.map((e) => e.toJson()).toList());
} catch (e) {
- return ServerErrorResponse(e.toString());
+ return InternalServerErrorResponse(e.toString());
}
}
@@ -78,6 +69,6 @@ Future _onBlogsPostRequest(RequestContext context) async {
} on CheckedFromJsonException catch (e) {
return BadRequestResponse(e.message);
} catch (e) {
- return ServerErrorResponse(e.toString());
+ return InternalServerErrorResponse(e.toString());
}
}
diff --git a/backend/routes/api/favorites/index.dart b/backend/routes/api/favorites/index.dart
index 3b4a1c2..e15df16 100644
--- a/backend/routes/api/favorites/index.dart
+++ b/backend/routes/api/favorites/index.dart
@@ -29,7 +29,7 @@ Future _onFavoritesGetRequest(RequestContext context) {
)
.then((r) => r.map(GetUserFavoriteBlogResponse.fromView).toList())
.then((res) => OkResponse(res.map((e) => e.toJson()).toList()))
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
}
Future _onFavoritesPostRequest(RequestContext context) async {
@@ -72,16 +72,16 @@ Future _onFavoritesPostRequest(RequestContext context) async {
),
)
.then((_) => OkResponse())
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
}
return db
- .query(
+ .execute(
'DELETE FROM favorite_blogs_userses '
'WHERE blog_id=@blogId AND user_id=@userId',
- {'blogId': request.blogId, 'userId': userView.id},
+ parameters: {'blogId': request.blogId, 'userId': userView.id},
)
.then((_) => OkResponse())
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
} on CheckedFromJsonException catch (e) {
return BadRequestResponse(e.message);
}
diff --git a/backend/routes/api/followings/index.dart b/backend/routes/api/followings/index.dart
index 0309c99..cb98b4b 100644
--- a/backend/routes/api/followings/index.dart
+++ b/backend/routes/api/followings/index.dart
@@ -42,13 +42,13 @@ Future _onFollowingPost(RequestContext context) async {
.firstOrNull;
if (existFollowing != null) {
return db
- .query(
+ .execute(
'DELETE FROM following_followers '
'WHERE following_id=@followingId AND follower_id=@followerId',
- {'followingId': userView.id, 'userId': userView.id},
+ parameters: {'followingId': userView.id, 'userId': userView.id},
)
.then((_) => OkResponse())
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
}
return db.followingFollowers
.insertOne(
@@ -58,10 +58,10 @@ Future _onFollowingPost(RequestContext context) async {
),
)
.then((_) => OkResponse())
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
} on CheckedFromJsonException catch (e) {
return BadRequestResponse(e.message);
} catch (e) {
- return ServerErrorResponse(e.toString());
+ return InternalServerErrorResponse(e.toString());
}
}
diff --git a/backend/routes/api/users/[id]/blogs.dart b/backend/routes/api/users/[id]/blogs.dart
index 183e189..80d9442 100644
--- a/backend/routes/api/users/[id]/blogs.dart
+++ b/backend/routes/api/users/[id]/blogs.dart
@@ -19,5 +19,5 @@ Future _onUsersByIdBlogsGet(RequestContext context, String id) {
.queryBlogs(QueryParams(where: 'creator_id=@id', values: {'id': id}))
.then((views) => views.map(GetUserBlogResponse.fromView))
.then((res) => OkResponse(res.map((e) => e.toJson()).toList()))
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
}
diff --git a/backend/routes/api/users/[id]/followers/index.dart b/backend/routes/api/users/[id]/followers/index.dart
index 16224be..7fadc5b 100644
--- a/backend/routes/api/users/[id]/followers/index.dart
+++ b/backend/routes/api/users/[id]/followers/index.dart
@@ -3,6 +3,7 @@ import 'package:stormberry/stormberry.dart';
import 'package:very_good_blog_app_backend/dtos/response/base_response_data.dart';
import 'package:very_good_blog_app_backend/dtos/response/users/followers/get_user_profile_response.dart';
import 'package:very_good_blog_app_backend/models/following_follower.dart';
+import 'package:very_good_blog_app_backend/models/user.dart';
/// @Allow(GET)
Future onRequest(RequestContext context, String id) {
@@ -15,17 +16,28 @@ Future onRequest(RequestContext context, String id) {
Future _onFollowersByIdGetRequest(
RequestContext context,
String id,
-) {
- return context
- .read()
- .followingFollowers
+) async {
+ final database = context.read();
+
+ return database.followingFollowers
.queryFollowingFollowers(
QueryParams(
where: 'follower_id = @id',
values: {'id': id},
),
)
- .then((result) => result.map(GetUserFollowerResponse.fromView))
+ .then(
+ (followingFollowerViews) async {
+ final followers = [];
+ for (final view in followingFollowerViews) {
+ final follower = await database.users.queryUser(view.followerId);
+ if (follower == null) continue;
+ followers.add(follower);
+ }
+ return followers;
+ },
+ )
+ .then((followers) => followers.map(GetUserFollowerResponse.fromView))
.then((res) => OkResponse(res.map((e) => e.toJson()).toList()))
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
}
diff --git a/backend/routes/api/users/[id]/followings/index.dart b/backend/routes/api/users/[id]/followings/index.dart
index f7533ff..3843a6f 100644
--- a/backend/routes/api/users/[id]/followings/index.dart
+++ b/backend/routes/api/users/[id]/followings/index.dart
@@ -3,6 +3,7 @@ import 'package:stormberry/stormberry.dart';
import 'package:very_good_blog_app_backend/dtos/response/base_response_data.dart';
import 'package:very_good_blog_app_backend/dtos/response/users/followings/get_user_following_response.dart';
import 'package:very_good_blog_app_backend/models/following_follower.dart';
+import 'package:very_good_blog_app_backend/models/user.dart';
/// @Allow(GET)
Future onRequest(RequestContext context, String id) {
@@ -16,16 +17,27 @@ Future _onFollowingsByIdGetRequest(
RequestContext context,
String id,
) {
- return context
- .read()
- .followingFollowers
+ final database = context.read();
+
+ return database.followingFollowers
.queryFollowingFollowers(
QueryParams(
where: 'following_id = @id',
values: {'id': id},
),
)
+ .then(
+ (followingFollowerViews) async {
+ final followings = [];
+ for (final view in followingFollowerViews) {
+ final following = await database.users.queryUser(view.followingId);
+ if (following == null) continue;
+ followings.add(following);
+ }
+ return followings;
+ },
+ )
.then((result) => result.map(GetUserFollowingResponse.fromView))
.then((res) => OkResponse(res.map((e) => e.toJson()).toList()))
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
}
diff --git a/backend/routes/api/users/[id]/profiles/index.dart b/backend/routes/api/users/[id]/profiles/index.dart
index 9782082..f147f1d 100644
--- a/backend/routes/api/users/[id]/profiles/index.dart
+++ b/backend/routes/api/users/[id]/profiles/index.dart
@@ -45,7 +45,7 @@ Future _onUserByIdGetRequest(
).toJson(),
),
)
- .catchError((_) => ServerErrorResponse());
+ .catchError((_) => InternalServerErrorResponse());
}
Future _onUserByIdPatchRequest(
@@ -73,7 +73,7 @@ Future _onUserByIdPatchRequest(
),
)
.then((_) => OkResponse())
- .onError((e, _) => ServerErrorResponse(e.toString()));
+ .onError((e, _) => InternalServerErrorResponse(e.toString()));
} on CheckedFromJsonException catch (e) {
return BadRequestResponse(e.message);
}