blob: 33dd442df6f8a48a836ab34ca876137132dd0108 [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import '../templates.dart';
import 'common.dart' as common;
/// A generator for a server app using `package:shelf`.
class ServerShelfGenerator extends DefaultGenerator {
ServerShelfGenerator()
: super(
'server-shelf', 'Server app', 'A server app using `package:shelf`',
categories: const ['dart', 'server']) {
addFile('.gitignore', common.gitignore);
addFile('analysis_options.yaml', common.analysisOptions);
addFile('CHANGELOG.md', common.changelog);
addFile('pubspec.yaml', _pubspec);
addFile('README.md', _readme);
addFile('Dockerfile', _dockerfile);
addFile('.dockerignore', _dockerignore);
addFile('test/server_test.dart', _test);
setEntrypoint(
addFile('bin/server.dart', _main),
);
}
@override
String getInstallInstructions(
String directory,
String scriptPath,
) =>
super.getInstallInstructions(
directory,
'bin/server',
);
}
final String _pubspec = '''
name: __projectName__
description: A server app using the shelf package and Docker.
version: 1.0.0
# homepage: https://www.example.com
environment:
${common.sdkConstraint}
dependencies:
args: ^2.0.0
shelf: ^1.1.0
shelf_router: ^1.0.0
dev_dependencies:
http: ^0.13.0
lints: ^1.0.0
test_process: ^2.0.0
test: ^1.15.0
''';
final String _readme = '''
A server app built using [Shelf](https://pub.dev/packages/shelf),
configured to enable running with [Docker](https://www.docker.com/).
This sample code handles HTTP GET requests to `/` and `/echo/<message>`
# Running the sample
## Running with the Dart SDK
You can run the example with the [Dart SDK](https://dart.dev/get-dart)
like this:
```
\$ dart run bin/server.dart
Server listening on port 8080
```
And then from a second terminal:
```
\$ curl http://0.0.0.0:8080
Hello, World!
\$ curl http://0.0.0.0:8080/echo/I_love_Dart
I_love_Dart
```
## Running with Docker
If you have [Docker Desktop](https://www.docker.com/get-started) installed, you
can build and run with the `docker` command:
```
\$ docker build . -t myserver
\$ docker run -it -p 8080:8080 myserver
Server listening on port 8080
```
And then from a second terminal:
```
\$ curl http://0.0.0.0:8080
Hello, World!
\$ curl http://0.0.0.0:8080/echo/I_love_Dart
I_love_Dart
```
You should see the logging printed in the first terminal:
```
2021-05-06T15:47:04.620417 0:00:00.000158 GET [200] /
2021-05-06T15:47:08.392928 0:00:00.001216 GET [200] /echo/I_love_Dart
```
''';
final String _main = r'''
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_router/shelf_router.dart';
// 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;
// Configure a pipeline that logs requests.
final _handler = Pipeline().addMiddleware(logRequests()).addHandler(_router);
// For running in containers, we respect the PORT environment variable.
final port = int.parse(Platform.environment['PORT'] ?? '8080');
final server = await serve(_handler, ip, port);
print('Server listening on port ${server.port}');
}
''';
final String _dockerfile = r'''
# Use latest stable channel SDK.
FROM dart:stable AS build
# Resolve app dependencies.
WORKDIR /app
COPY pubspec.* ./
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
# 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/
# Start server.
EXPOSE 8080
CMD ["/app/bin/server"]
''';
final String _dockerignore = r'''
.dockerignore
Dockerfile
build/
.dart_tool/
.git/
.github/
.gitignore
.idea/
.packages
''';
final String _test = r'''
import 'package:http/http.dart';
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';
void main() {
final port = '8080';
final host = 'http://0.0.0.0:$port';
setUp(() async {
await TestProcess.start(
'dart',
['run', 'bin/server.dart'],
environment: {'PORT': port},
);
});
test('Root', () async {
final response = await get(Uri.parse(host + '/'));
expect(response.statusCode, 200);
expect(response.body, 'Hello, World!\n');
});
test('Echo', () async {
final response = await get(Uri.parse(host + '/echo/hello'));
expect(response.statusCode, 200);
expect(response.body, 'hello\n');
});
test('404', () async {
final response = await get(Uri.parse(host + '/foobar'));
expect(response.statusCode, 404);
});
}
''';