Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ PEXELS_API_KEY=
DB_HOST=
DB_USERNAME=
DB_DATABASE=
DB_PASSWORD=
DB_PASSWORD=

# Required for generating & validating auth tokens
APP_KEY=
13 changes: 12 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ jobs:
uses: actions/checkout@v3

- uses: dart-lang/setup-dart@v1.3
- uses: subosito/flutter-action@v2
with:
channel: stable
- uses: bluefireteam/melos-action@v3


- name: Check formatting
run: dart format . --line-length=120 --set-exit-if-changed

- name: Check linting
run: |
dart run build_runner build
cd packages/shared && dart run build_runner build --delete-conflicting-outputs
dart analyze . --fatal-infos

test:
Expand All @@ -37,14 +43,19 @@ jobs:
uses: actions/checkout@v3

- uses: dart-lang/setup-dart@v1.3
- uses: subosito/flutter-action@v2
with:
channel: stable
- uses: bluefireteam/melos-action@v3

- name: Bootstrap
run: |
dart pub global activate coverage
dart run build_runner build --delete-conflicting-outputs
cd packages/shared && dart run build_runner build --delete-conflicting-outputs

- name: Setup Test Database
run: dart run yaroorm migrate
run: dart run yaroorm_cli migrate --connection=local

- name: Run Tests
run: |
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@ $ dart pub get && dart run build_runner build --delete-conflicting-outputs

### Migrate Database

- For local dev, execute migrations on sqlite database using the command below

```shell
dart run yaroorm_cli migrate --connection=local
```

- For production database, you can run this.

```shell
$ dart run yaroorm migrate
```

```shell
┌──────────────────────────────┬──────────────────────────────┐
│ Migration │ Status │
├──────────────────────────────┼──────────────────────────────┤
│ create_users_table │ ✅ migrated │
│ Migration │ Status │
├──────────────────────────────┼──────────────────────────────┤
create_articles_table │ ✅ migrated │
initial_table_setup │ ✅ migrated
└──────────────────────────────┴──────────────────────────────┘
```

### Start Server

```shell
$ dart run --enable-asserts
$ dart run
```

### Tests
Expand Down
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ linter:
analyzer:
exclude:
- "**.reflectable.dart"
- frontend/
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints

Expand Down
1 change: 0 additions & 1 deletion database/config.dart

This file was deleted.

7 changes: 3 additions & 4 deletions database/database.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions database/migrations/2024_04_20_003612_initial_setup.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import 'package:backend/src/models/article.dart';
import 'package:backend/src/models/user.dart';
import 'package:backend/src/models.dart';
import 'package:yaroorm/yaroorm.dart';

class InitialTableSetup extends Migration {
@override
void up(List<Schema> schemas) {
schemas.addAll([UserSchema, ArticleSchema]);
schemas.addAll([ServerUserSchema, ServerArticleSchema]);
}

@override
void down(List<Schema> schemas) {
schemas.addAll([
Schema.dropIfExists(UserSchema),
Schema.dropIfExists(ArticleSchema),
Schema.dropIfExists(ServerUserSchema),
Schema.dropIfExists(ServerArticleSchema),
]);
}
}
4 changes: 2 additions & 2 deletions lib/app/middlewares/api_auth_middleware.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:backend/src/models.dart';
import 'package:pharaoh/pharaoh_next.dart';

import '../../src/models/user.dart';
import '../../src/services/auth_service.dart';

class ApiAuthMiddleware extends ClassMiddleware {
Expand All @@ -13,7 +13,7 @@ class ApiAuthMiddleware extends ClassMiddleware {
final userId = _authService.validateRequest(req);
if (userId == null) return next(res.unauthorized());

final user = await UserQuery.findById(userId);
final user = await ServerUserQuery.findById(userId);
if (user == null) return next(res.unauthorized());

return next(req..auth = user);
Expand Down
31 changes: 21 additions & 10 deletions lib/app/middlewares/core_middleware.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:logger/logger.dart';
import 'package:logging/logging.dart';
import 'package:pharaoh/pharaoh.dart';
import 'package:pharaoh/pharaoh_next.dart';
import 'package:shared/shared.dart';

class CoreMiddleware extends ClassMiddleware {
late Middleware _webMdw;
Expand All @@ -11,17 +12,27 @@ class CoreMiddleware extends ClassMiddleware {
final cookieConfig = app.instanceOf<CookieOpts>();
final cookieParserMdw = cookieParser(opts: cookieConfig);

/// setup logger
loggerMdw(Request req, Response res, NextFunction next) {
_logger.i('Req: ${req.method.name}:${req.path}');
next();
}
corsMiddleware(Request req, Response res, NextFunction next) {
res = res
..header('Access-Control-Allow-Origin', appEnv.frontendURL.toString())
..header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
..header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
..header('Access-Control-Allow-Credentials', 'true')
..header('Access-Control-Max-Age', '3600');

if (req.method == HTTPMethod.OPTIONS) {
return next(res.status(200).end());
}

if (app.config.environment == 'development') {
_webMdw = loggerMdw.chain(cookieParserMdw);
} else {
_webMdw = cookieParserMdw;
return next(res);
}

_webMdw = corsMiddleware.chain(cookieParserMdw).chain((req, res, next) {
if (isTestMode) return next();

_logger.fine('${req.method.name}:${req.path}');
next();
});
}

@override
Expand Down
19 changes: 8 additions & 11 deletions lib/app/providers/provide_core.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:backend/src/services/services.dart';
import 'package:logger/logger.dart';
import 'package:logging/logging.dart';
import 'package:pharaoh/pharaoh.dart';
import 'package:pharaoh/pharaoh_next.dart';

Expand All @@ -12,15 +12,12 @@ class CoreProvider extends ServiceProvider {

app.singleton<AuthService>(AuthService(app.config.key, app.config.url));

app.singleton<Logger>(Logger(printer: PrettyPrinter(), filter: _CustomLogFilter(app.config.isDebug)));
}
}

class _CustomLogFilter extends LogFilter {
final bool loggingEnabled;
Logger.root
..level = Level.ALL
..onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});

_CustomLogFilter(this.loggingEnabled);

@override
bool shouldLog(LogEvent event) => loggingEnabled;
app.singleton<Logger>(Logger(config.environment));
}
}
4 changes: 2 additions & 2 deletions lib/app/providers/provide_database.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:logger/logger.dart';
import 'package:logging/logging.dart';
import 'package:pharaoh/pharaoh_next.dart';
import 'package:yaroorm/yaroorm.dart';

Expand All @@ -8,6 +8,6 @@ class DatabaseServiceProvider extends ServiceProvider {
await DB.defaultDriver.connect();

final logger = app.instanceOf<Logger>();
logger.d('Using ${DB.defaultConnection.info.name} database');
logger.info('Using ${DB.defaultConnection.info.name} database');
}
}
8 changes: 2 additions & 6 deletions lib/app/providers/provide_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:async';
import 'package:pharaoh/pharaoh_next.dart';

import '../routes/api.dart' as api;
import '../routes/api.dart';
import '../routes/web.dart' as web;

class RouteServiceProvider extends ServiceProvider {
Expand All @@ -14,11 +13,8 @@ class RouteServiceProvider extends ServiceProvider {
/*|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------*/

publicRoutes,

//
Route.middleware('api:auth').group('api', api.authRoutes),
api.publicRoutes,
api.authRoutes,

/*|--------------------------------------------------------------------------
| Web Routes
Expand Down
8 changes: 4 additions & 4 deletions lib/app/routes/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ final publicRoutes = Route.group('api', [
]),
]);

List<RouteDefinition> authRoutes = [
/// Users
final authRoutes = Route.middleware('api:auth').group('api', [
// Users
Route.group('users', [
Route.get('/', (UserController, #index)),
Route.get('/me', (UserController, #currentUser)),
]),

/// Articles
// Articles
Route.group('articles', [
Route.post('/', (ArticleController, #create)),
Route.put('/<articleId>', (ArticleController, #update)),
Route.delete('/<articleId>', (ArticleController, #delete)),
]),
];
]);
5 changes: 4 additions & 1 deletion lib/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';

import 'package:backend/src/utils/config.dart';
import 'package:logging/logging.dart';
import 'package:pharaoh/pharaoh_next.dart';

import 'app/middlewares/core_middleware.dart';
Expand All @@ -13,7 +14,7 @@ export 'src/dto/dto.dart';

final blogApp = App(appConfig);

class App extends ApplicationFactory {
class App extends ApplicationFactory with AppInstance {
App(super.appConfig);

@override
Expand Down Expand Up @@ -41,6 +42,8 @@ class App extends ApplicationFactory {
return response.json(exception.errorBody, statusCode: HttpStatus.badRequest);
}

app.instanceOf<Logger>().severe(exception, null, error.trace);

return super.onApplicationException(error, request, response);
}
}
24 changes: 15 additions & 9 deletions lib/src/controllers/article_controller.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import 'dart:isolate';

import 'package:backend/src/dto/article_dto.dart';
import 'package:backend/src/models.dart';
import 'package:backend/src/services/services.dart';
import 'package:pharaoh/pharaoh_next.dart';
import 'package:shared/models.dart';
import 'package:yaroorm/yaroorm.dart';

import '../models/article.dart';
import '../models/user.dart';
import '../utils/utils.dart';

class ArticleController extends HTTPController {
final ArticleService _articleService;
Expand All @@ -22,13 +26,15 @@ class ArticleController extends HTTPController {
}

Future<Response> create(@body CreateArticleDTO data) async {
var imageUrl = data.imageUrl;
if (app.config.isDebug) {
imageUrl ??= 'https://dart.dev/assets/shared/dart-logo-for-shares.png';
}
final imageUrl = data.imageUrl ?? await Isolate.run(() => getRandomImage(data.title));

final article = await _articleService.createArticle(user, data, imageUrl: imageUrl);
return jsonResponse(_articleResponse(article));
final article = await user.articles.insert(NewServerArticleForServerUser(
title: data.title,
description: data.description,
imageUrl: Value(imageUrl),
));

return response.json(_articleResponse(article));
}

Future<Response> update(@param int articleId, @body CreateArticleDTO data) async {
Expand All @@ -44,5 +50,5 @@ class ArticleController extends HTTPController {

Map<String, dynamic> _articleResponse(Article article) => {'article': article.toJson()};

User get user => request.auth as User;
ServerUser get user => request.auth as ServerUser;
}
Loading
Loading