From 737285363a02c87d7922ca0df3896f96c7010055 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Thu, 21 Apr 2022 05:59:37 +0000 Subject: [PATCH] Initial commit --- .dockerignore | 9 ++ .gitignore | 6 + CHANGELOG.md | 3 + Dockerfile | 21 +++ README.md | 49 ++++++ analysis_options.yaml | 30 ++++ bin/server.dart | 70 ++++++++ pubspec.lock | 369 ++++++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 20 +++ test/server_test.dart | 32 ++++ 10 files changed, 609 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 bin/server.dart create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 test/server_test.dart diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..21504f8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.dockerignore +Dockerfile +build/ +.dart_tool/ +.git/ +.github/ +.gitignore +.idea/ +.packages diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c8a157 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build output. +build/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c333dee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# 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"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..e695d9d --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +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/` + +# 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 +``` diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..dee8927 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/bin/server.dart b/bin/server.dart new file mode 100644 index 0000000..77b7254 --- /dev/null +++ b/bin/server.dart @@ -0,0 +1,70 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart'; +import 'package:shelf_router/shelf_router.dart'; + +// Load confirmation token from env +final confirmationToken = Platform.environment['CONFIRMATION_TOKEN']; +// Load secret key from env +final secretKey = Platform.environment['SECRET_KEY']; + +// Configure routes. +final _router = Router() + ..get('/', _rootHandler) + ..post('/vk', _vkHandler); + +Response _rootHandler(Request req) { + return Response.ok('Hello, World!\n'); +} + +Future _vkHandler(Request req) async { + // POST request should contain a JSON body. + // JSON must contain a 'type' field. + // The value of the 'type' field must be one of the following: + // 'confirmation' + // 'wall_post_new' + // If we recieve a 'confirmation' request, we should respond with a confirmationToken. + // If we recieve a 'wall_post_new' request, we should validate if 'secret' field equals + // secretKey and respond with an 'ok' string, printing request in the console. + + final requestBody = await req.readAsString(); + final requestJson = json.decode(requestBody); + final requestType = requestJson['type']; + + if (requestType == 'confirmation') { + return Response.ok(confirmationToken); + } else if (requestType == 'wall_post_new') { + if (requestJson['secret'] == secretKey) { + print(requestBody); + return Response.ok('ok'); + } else { + return Response.ok('invalid secret'); + } + } else { + return Response.ok('invalid request type'); + } +} + +// Future _postOnMastodon(reqJson) async { +// // Called to repost the post on Mastodon on vk. +// // reqJson contains the 'object' field of the VK post. +// // This object has the 'text' field with the post text. +// // Also it contains the 'attachments' field with the attachments. +// // The attachments field is an array of objects. +// } + +void main(List 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}'); +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..03943a0 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,369 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "39.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + args: + dependency: "direct main" + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.6" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + http: + dependency: "direct dev" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.4" + http_methods: + dependency: transitive + description: + name: http_methods + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" + lints: + dependency: "direct dev" + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shelf: + dependency: "direct main" + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + shelf_router: + dependency: "direct main" + description: + name: shelf_router + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.21.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.9" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.13" + test_process: + dependency: "direct dev" + description: + name: test_process + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "8.2.2" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" +sdks: + dart: ">=2.14.4 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..3e6f706 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,20 @@ +name: mastodon_vk_reposter +description: A server app using the shelf package and Docker. +version: 1.0.0 +# homepage: https://www.example.com + +environment: + sdk: '>=2.14.4 <3.0.0' + + +dependencies: + args: ^2.0.0 + dio: ^4.0.6 + 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 diff --git a/test/server_test.dart b/test/server_test.dart new file mode 100644 index 0000000..67bbd2a --- /dev/null +++ b/test/server_test.dart @@ -0,0 +1,32 @@ +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); + }); +}