Compare commits

...

21 Commits

Author SHA1 Message Date
remrem 18f65d6555 idk
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 312e73de15 test linode
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 16639a0a25 upload and download file works
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 40f39801cd Merge branch 'master' of https://codefirst.iut.uca.fr/git/PassWorld/PassWorld-API
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 8eb6f3fd23 test push
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 19d8ee4d2a save work
2 years ago
remrem 4ee56387b3 new port
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 539ea9dcb0 test deploy
continuous-integration/drone/push Build is passing Details
2 years ago
remrem f281e96569 Merge branch 'master' of https://codefirst.iut.uca.fr/git/PassWorld/PassWorld-API
continuous-integration/drone/push Build was killed Details
2 years ago
remrem 4087776924 idk
2 years ago
remrem f5b30dd396 Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 30f2e04cfb Update '.drone.yml'
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 35ceacf978 🔒 add authenticationCheck function
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 3325f8679c add changePassword
continuous-integration/drone/push Build is passing Details
2 years ago
remrem d1d2233f69 🚀 test ADMINS tag for drone
continuous-integration/drone/push Build is passing Details
2 years ago
remrem cc15fc4f51 add account deletion
continuous-integration/drone/push Build is failing Details
2 years ago
remrem e740bfe9c8 🔥 resolve conflicts
continuous-integration/drone/push Build is passing Details
2 years ago
remrem 133757e9c4 update account creation + db creation, add updateMail
2 years ago
remrem 503e0092b0 Delete 'pubspec.lock'
continuous-integration/drone/push Build is passing Details
3 years ago
remrem 30e3ac22a9 Delete 'CHANGELOG.md'
continuous-integration/drone/push Build is failing Details
3 years ago
remrem 5123630aee Update 'README.md'
continuous-integration/drone/push Build is failing Details
3 years ago

@ -35,7 +35,7 @@ steps:
CONTAINERNAME: passworld-api
COMMAND: create
OVERWRITE: true
CODEFIRST_CLIENTDRONE_ENV_DB_SERVER: db_accounts-remiarnal
CODEFIRST_CLIENTDRONE_ENV_DB_SERVER: PassWorld-db_accounts
CODEFIRST_CLIENTDRONE_ENV_DB_USER:
from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_DB_PASSWORD:
@ -55,4 +55,5 @@ steps:
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_USER:
from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD:
from_secret: db_password
from_secret: db_password
ADMINS: remi.arnal

2
.gitignore vendored

@ -2,6 +2,8 @@
.dart_tool/
.packages
# server.dart compiled executable
pubspec.lock
server
server.exe
# Conventional directory for build output.
build/

@ -1,3 +0,0 @@
## 1.0.0
- Initial version.

@ -3,7 +3,7 @@
A server app built using [Shelf](https://pub.dev/packages/shelf),
configured to enable running with [Docker](https://www.docker.com/).
## Links
## Links (Don't works anymore :( )
Here is the [API Link](https://codefirst.iut.uca.fr/containers/passworld-api-remiarnal/)
Cheat sheet that i use for the api status code : [HTTP Status Code Cheat Sheet](https://www.websiterating.com/resources/http-status-codes-cheat-sheet/)

@ -10,14 +10,16 @@ final _router = Router()
// GET
..get('/', API.rootHandler)
..get('/admin/users', API.getAllUsers)
..post('/user/salt', API.getSalt)
// POST (EN VRAI C'EST DES GET AVEC UN BODY)
..post('/user/password-file', API.downloadPasswordDb)
..post('/user/password-file-download', API.downloadPasswordDb)
..post('/auth', API.authenticator)
..post('/user/account', API.createAccount) // vrai post
// PUT
..put('/user/master-password', API.changeMasterPassword)
..put('/user/password-file', API.uploadPasswordDb)
..post('/user/password-file', API.uploadPasswordDb)
..put('/user/change-mail', API.changeMail)
..put('/user/password', API.changeMasterPassword)
// DELETE
..delete('/user/account', API.deleteAccount);
@ -49,7 +51,7 @@ void main(List<String> args) async {
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 port = int.parse(Platform.environment['PORT'] ?? '8989');
final server = await serve(handler, ip, port);
print('Server listening on port ${server.port}');
}

@ -1,3 +1,4 @@
import 'dart:io';
import 'package:passworld_api/db_to_api.dart';
import 'package:postgres/postgres.dart';
import 'package:shelf/shelf.dart';
@ -7,57 +8,43 @@ import 'package:passworld_api/database/accounts_to_postgres.dart';
// Class for all static function that handles api routes
class API {
/*---------------|
|-------GET------|
|---------------*/
// Default response for /
static Response rootHandler(Request req) {
return Response.ok('Greetings from PassWorld!\n');
}
// Check for authentication
static Future<Response> authenticator(Request req) async {
final List<String> required = ["email", "password"];
static Future<Response> getSalt(Request req) async {
final List<String> required = ["email"];
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
try {
await AccountsToPostgres.selectHashById(body[required[0]]);
String salt =
await AccountsToPostgres.selectSaltByMail(body[required[0]]);
return Response(200, body: salt);
} catch (e) {
return Response(404,
body: 'Not Found'); // no hash found -> 404 (Not Found)
return Response(204, body: 'Account already existing'); // No content
}
return Response.ok('Succesfully Authenticated'); // 200 (Ok)
} else {
return Response.badRequest(
body: 'Bad password or email !'); // 400 (Bad Request)
return Response.badRequest(body: 'bad body');
}
}
// Download sqlite password file
static Response downloadPasswordDb(Request req) {
final mail = req.params['mail'];
final password = req.params['cyphered_password_hash'];
// Database query -> return file (List<int>)
// Create stream from List<int>
// Rename file -> db_password_<mail>_<date>
// Send file
return Response.ok("");
/*
Stream<List<int>> fileStream = file.openRead();
return Response.ok(fileStream, headers: {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment, filename="$reqFile"'
});
*/
// Check for authentication
static Future<Response> authenticator(Request req) async {
final List<String> required = ["email", "password"];
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
if (await checkAuthentication(body[required[0]], body[required[1]])) {
return Response.ok('Succesfully Authenticated');
} else {
return Response.unauthorized('Bad password or email !'); // 401
}
} else {
return Response.badRequest(body: 'bad body'); // 401
}
}
/*---------------|
|------POST------|
|---------------*/
// Create account
static Future<Response> createAccount(Request req) async {
@ -65,14 +52,14 @@ class API {
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
// List<String> twofa = body[required[3]];
try {
await AccountsToPostgres.create(body[required[0]], body[required[1]],
body[required[2]] /*, twofa*/);
await AccountsToPostgres.createAccount(
body[required[0]], body[required[1]], body[required[2]]);
} catch (e) {
return Response(409,
body: 'Account already existing'); // 409 (Conflict)
}
print("✅ Account succesfully created");
return Response(201,
body: 'Account successfully created'); // 201 (Created)
} else {
@ -80,38 +67,130 @@ class API {
}
}
/*---------------|
|-------PUT------|
|---------------*/
// Delete Account
static Future<Response> deleteAccount(Request req) async {
final List<String> required = ["email", "password"];
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
try {
if (await checkAuthentication(body[required[0]], body[required[1]])) {
await AccountsToPostgres.deleteAccount(body[required[0]]);
} else {
return Response(403,
body:
'You haven\'t provided the good password or mail'); // 403 (Forbidden)
}
} catch (e, s) {
print("Exception $e");
print("Stacktrace $s");
return Response(409,
body: 'There was a problem with deletion'); // 409 (Conflict)
}
print("✅ Account succesfully deleted");
return Response(200, body: 'Account successfully deleted'); // 200 (OK)
} else {
return Response.badRequest(body: 'Bad request'); // 400 (Bad Request)
}
}
// Update master password
static Response changeMasterPassword(Request req) {
return Response.ok("master password chnaged");
static Future<Response> changeMasterPassword(Request req) async {
final List<String> required = ["email", "newPassword", "newSalt"];
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
try {
await AccountsToPostgres.updatePassword(
body[required[0]], body[required[1]], body[required[2]]);
} catch (e) {
return Response(403,
body: 'This is not the good password'); // 403 (Forbidden)
}
return Response(201,
body: 'user\'s password succesfully changed'); // 201 (Created)
} else {
return Response.badRequest(body: 'Bad request'); // 400 (Bad Request)
}
}
// Update mail
static Response changeMail(Request req) {
return Response.ok("master password chnaged");
static Future<Response> changeMail(Request req) async {
final List<String> required = ["email", "newMail"];
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
try {
await AccountsToPostgres.updateMail(
body[required[0]], body[required[1]]);
} catch (e) {
return Response(403,
body: 'This is not the good password'); // 403 (Forbidden)
}
return Response(201,
body: 'user\'s mail succesfully changed'); // 201 (Created)
} else {
return Response.badRequest(body: 'Bad request'); // 400 (Bad Request)
}
}
// Upload sqlite password file
static Response uploadPasswordDb(Request req) {
return Response.ok("");
static Future<Response> uploadPasswordDb(Request req) async {
final List<String> required = ["email", "password", "file"];
final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) {
try {
if (await checkAuthentication(body[required[0]], body[required[1]])) {
String fileAsBytes = body[required[2]];
var arrayBytes = fileAsBytes.split(',');
arrayBytes.removeLast();
List<int> arrayBytes2 = arrayBytes.map(int.parse).toList();
await AccountsToPostgres.updatePasswordFile(
body[required[0]], arrayBytes2);
} else {
return Response(403); // 403 (Forbidden)
}
} catch (e, s) {
print("Exception $e");
print("Stacktrace $s");
return Response(409,
body: 'There was a problem with upload'); // 409 (Conflict)
}
print("✅ PassWord file succesfully uploaded");
return Response(201,
body: 'PassWord file succesfully uploaded'); // 20 (OK)
} else {
return Response.badRequest(body: 'Bad request'); // 400 (Bad Request)
}
}
/*---------------|
|-----DELETE-----|
|---------------*/
// Download sqlite password file
static Future<Response> downloadPasswordDb(Request req) async {
final List<String> required = ["email", "password"];
final body = await bodyToJson(req);
// Delete account
static Response deleteAccount(Request req) {
return Response.ok("");
if (await checkRequiredFields(required, body)) {
try {
if (await checkAuthentication(body[required[0]], body[required[1]])) {
List<int> file =
await AccountsToPostgres.getPasswordFile(body[required[0]]);
print("✅ PassWord file succesfully downloaded");
return Response(200, body: file.toString());
} else {
return Response(403); // 403 (Forbidden)
}
} catch (e, s) {
print("Exception $e");
print("Stacktrace $s");
return Response(409,
body: 'There was a problem with upload'); // 409 (Conflict)
} // 200 (OK)
} else {
return Response.badRequest(body: 'Bad request'); // 400 (Bad Request)
}
}
/*---------------|
|-------MISC-----|
|---------------*/
// Check if required fields are in req body
static Future<bool> checkRequiredFields(
List<String> fields, Map<String, dynamic> body) async {
@ -133,6 +212,44 @@ class API {
return json.decode(tmp);
}
static Future<bool> checkAuthentication(
String givedMail, String givedPassword) async {
try {
if (!await checkMail(givedMail)) return false;
} catch (e) {
// catch if there is nothing in result of checkMail
return false;
}
if (!await checkPassword(givedMail, givedPassword)) return false;
print("authentication successed !!!");
return true;
}
static Future<bool> checkPassword(
String givedMail, String givedPassword) async {
print("check hash...");
var hash = await AccountsToPostgres.selectHashByMail(givedMail);
if (hash == givedPassword) {
print("hash is good");
return true;
}
print("hash is bad");
return false;
}
static Future<bool> checkMail(String givedMail) async {
print("check mail...");
var mail = await AccountsToPostgres.selectMailByMail(givedMail);
if (mail == givedMail) {
print("mail is good");
return true;
}
print("mail is bad");
return false;
}
//
// ADMIN
//

@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'package:postgres/postgres.dart';
@ -9,118 +8,160 @@ class AccountsToPostgres {
// username: 'pass', password: '1p2a3s4s5');
/* Dev RemRem */
// static final connection = PostgreSQLConnection("localhost", 5432, 'passworld',
// username: 'hel', password: '');
static final connection = PostgreSQLConnection("localhost", 5432, 'passworld',
username: 'hel', password: '');
/* Production */
static final connection = PostgreSQLConnection(
Platform.environment["DB_SERVER"]!,
5432,
Platform.environment["DB_DATABASE"]!,
username: Platform.environment["DB_USER"],
password: Platform.environment["DB_PASSWORD"]);
// static final connection = PostgreSQLConnection(
// Platform.environment["DB_SERVER"]!,
// 5432,
// Platform.environment["DB_DATABASE"]!,
// username: Platform.environment["DB_USER"],
// password: Platform.environment["DB_PASSWORD"]);
AccountsToPostgres() {
//initConnection();
}
// Open connection to database
static Future<void> openConnection() async {
await connection.open().then((value) {
print("🟢 PassWorld DB connection opened");
});
}
// Close connection to database
static void closeConnection() async {
connection.close().then((value) {
print("🔴 PassWorld DB connection closed");
});
}
// Create tables and other things for the database
static Future<void> createAccountTable() async {
await openConnection();
await connection
.query(
"CREATE TABLE IF NOT EXISTS \"Account\"(id TEXT PRIMARY KEY,hash TEXT NOT NULL,salt TEXT NOT NULL,twofa VARCHAR(50)[],passwords INTEGER[])")
.then((value) {
print("🟦 Account Table Created");
});
await connection.query("""
CREATE TABLE IF NOT EXISTS \"Account\"(
id INT PRIMARY KEY,
mail TEXT NOT NULL UNIQUE,
hash TEXT NOT NULL,
salt TEXT NOT NULL,
twofa VARCHAR(50)[],
password_file INTEGER[]
)""");
await connection.query("""
CREATE SEQUENCE IF NOT EXISTS plus1id
INCREMENT 1
START 1""");
print("🟦 Account Table Created");
}
// Add support for twoFa if needed
static Future<void> create(String email, String hash,
String salt /*, List<String> twoFaStr*/) async {
await connection.query("INSERT INTO \"Account\" VALUES(@id,@hash,@salt)",
substitutionValues: {
"id": email,
"hash": hash,
"salt": salt /*,
"twofa": twoFaStr*/
});
print("✅ Account succesfully created");
// Create user account
static Future<void> createAccount(
String mail, String hash, String salt) async {
await connection.query(
"INSERT INTO \"Account\" VALUES(nextval('plus1id'),@mail,@hash,@salt)",
substitutionValues: {"mail": mail, "hash": hash, "salt": salt});
}
static Future<String> selectHashById(String id) async {
static Future<void> deleteAccount(String mail) async {
await connection.query("DELETE FROM \"Account\" WHERE mail=@mail",
substitutionValues: {"mail": mail});
}
// get user passord hash by mail
static Future<String> selectHashByMail(String mail) async {
List<List<dynamic>> results = await connection.query(
"SELECT hash FROM \"Account\" WHERE id=@identifiant",
substitutionValues: {"identifiant": id});
"SELECT hash FROM \"Account\" WHERE mail=@mail",
substitutionValues: {"mail": mail});
closeConnection();
return results[0][0];
}
static Future<void> updatePass(
String identifiant, String hash, String salt) async {
if (selectHashById(identifiant) == null) {
// check if mail is already used in database
static Future<String> selectMailByMail(String mail) async {
List<List<dynamic>> results = await connection.query(
"SELECT mail FROM \"Account\" WHERE mail=@mail",
substitutionValues: {"mail": mail});
return results[0][0];
}
// check if mail is already used in database
static Future<String> selectSaltByMail(String mail) async {
List<List<dynamic>> results = await connection.query(
"SELECT salt FROM \"Account\" WHERE mail=@mail",
substitutionValues: {"mail": mail});
return results[0][0];
}
// Update user password
static Future<void> updatePassword(
String mail, String newHash, String newSalt) async {
if (selectHashByMail(mail) == null) {
return;
} else {
await connection.query(
"UPDATE \"Account\" SET hash=@h, salt=@s WHERE id=@identifiant",
"UPDATE \"Account\" SET hash=@newHash and salt=@salt WHERE mail=@mail",
substitutionValues: {
"identifiant": identifiant,
"h": hash,
"s": salt
"mail": mail,
"newHash": newHash,
"newSalt": newSalt
});
print("✅ Passworld succesfully updated");
}
}
static Future<void> updateFilePass(
String identifiant, File passwordFile) async {
List<int> passwordBlob =
utf8.encode(await passwordFile.readAsString(encoding: utf8));
// Update user password file
static Future<void> updatePasswordFile(
String mail, List<int> passwordFile) async {
await connection.query(
"UPDATE \"Account\" SET password_file=@p WHERE mail=@mail",
substitutionValues: {"mail": mail, "p": passwordFile});
}
if (selectHashById(identifiant) == null) {
return;
} else {
await connection.query(
"UPDATE \"Account\" SET passwords=@p WHERE id=@identifiant",
substitutionValues: {"identifiant": identifiant, "p": passwordBlob});
}
static Future<List<int>> getPasswordFile(String mail) async {
List<List<dynamic>> data = await connection.query(
"SELECT password_file FROM \"Account\" WHERE mail=@mail",
substitutionValues: {
"mail": mail,
});
return data[0][0];
}
static Future<void> updateTwoFa(String identifiant, List<String> tfa) async {
// Update user twoFa
static Future<void> updateTwoFa(String mail, List<String> tfa) async {
List<String> twoFaStr = List.empty(growable: true);
if (selectHashById(identifiant) == null) {
if (selectHashByMail(mail) == null) {
return;
} else {
await connection.query(
"UPDATE \"Account\" SET twofa=@tfa WHERE id=@identifiant",
substitutionValues: {"identifiant": identifiant, "tfa": tfa});
substitutionValues: {"identifiant": mail, "tfa": tfa});
}
}
static Future<void> deleteById(String id) async {
await connection.query("DELETE FROM \"Account\" WHERE id=@identifiant",
substitutionValues: {"identifiant": id});
// Update user mail
static Future<void> updateMail(String mail, String newMail) async {
if (selectHashByMail(mail) == null) {
return;
} else {
await connection.query(
"UPDATE \"Account\" SET mail=@newMail WHERE mail=@mail",
substitutionValues: {"newMail": newMail, "mail": mail});
print("✅ Mail succesfully updated");
}
}
//
// ADMIN
//
// ADMIN: get infos on all users
static Future<PostgreSQLResult> getAllUsers() async {
PostgreSQLResult res =
await connection.query("SELECT id, hash, salt from \"Account\"");
await connection.query("SELECT mail, hash, salt from \"Account\"");
print("🟥 ADMIN: get all users");
return res;
}

@ -1,390 +0,0 @@
# 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: "50.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.0"
args:
dependency: "direct main"
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.10.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
buffer:
dependency: transitive
description:
name: buffer
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.17.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.4"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
http:
dependency: "direct dev"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.5"
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.1"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
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.5"
lints:
dependency: "direct dev"
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.13"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
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.1.0"
path:
dependency: "direct main"
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
postgres:
dependency: "direct main"
description:
name: postgres
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.2"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
sasl_scram:
dependency: transitive
description:
name: sasl_scram
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1"
saslprep:
dependency: transitive
description:
name: saslprep
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
shelf:
dependency: "direct main"
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
shelf_router:
dependency: "direct main"
description:
name: shelf_router
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.3"
shelf_static:
dependency: transitive
description:
name: shelf_static
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.11"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
test:
dependency: "direct dev"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.22.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.16"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.20"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
unorm_dart:
dependency: transitive
description:
name: unorm_dart
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "9.4.0"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.18.2 <3.0.0"

@ -8,6 +8,7 @@ environment:
dependencies:
args: ^2.0.0
mime: ^1.0.3
path: ^1.8.2
postgres: ^2.5.2
shelf: ^1.1.0

Loading…
Cancel
Save