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 CONTAINERNAME: passworld-api
COMMAND: create COMMAND: create
OVERWRITE: true OVERWRITE: true
CODEFIRST_CLIENTDRONE_ENV_DB_SERVER: db_accounts-remiarnal CODEFIRST_CLIENTDRONE_ENV_DB_SERVER: PassWorld-db_accounts
CODEFIRST_CLIENTDRONE_ENV_DB_USER: CODEFIRST_CLIENTDRONE_ENV_DB_USER:
from_secret: db_user from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_DB_PASSWORD: CODEFIRST_CLIENTDRONE_ENV_DB_PASSWORD:
@ -56,3 +56,4 @@ steps:
from_secret: db_user from_secret: db_user
CODEFIRST_CLIENTDRONE_ENV_POSTGRES_PASSWORD: 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/ .dart_tool/
.packages .packages
# server.dart compiled executable # server.dart compiled executable
pubspec.lock
server server
server.exe
# Conventional directory for build output. # Conventional directory for build output.
build/ 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), A server app built using [Shelf](https://pub.dev/packages/shelf),
configured to enable running with [Docker](https://www.docker.com/). 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/) 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/) 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
..get('/', API.rootHandler) ..get('/', API.rootHandler)
..get('/admin/users', API.getAllUsers) ..get('/admin/users', API.getAllUsers)
..post('/user/salt', API.getSalt)
// POST (EN VRAI C'EST DES GET AVEC UN BODY) // 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('/auth', API.authenticator)
..post('/user/account', API.createAccount) // vrai post ..post('/user/account', API.createAccount) // vrai post
// PUT // PUT
..put('/user/master-password', API.changeMasterPassword) ..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/change-mail', API.changeMail)
..put('/user/password', API.changeMasterPassword)
// DELETE // DELETE
..delete('/user/account', API.deleteAccount); ..delete('/user/account', API.deleteAccount);
@ -49,7 +51,7 @@ void main(List<String> args) async {
final handler = Pipeline().addMiddleware(logRequests()).addHandler(_router); final handler = Pipeline().addMiddleware(logRequests()).addHandler(_router);
// For running in containers, we respect the PORT environment variable. // 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); final server = await serve(handler, ip, port);
print('Server listening on port ${server.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:passworld_api/db_to_api.dart';
import 'package:postgres/postgres.dart'; import 'package:postgres/postgres.dart';
import 'package:shelf/shelf.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 for all static function that handles api routes
class API { class API {
/*---------------|
|-------GET------|
|---------------*/
// Default response for / // Default response for /
static Response rootHandler(Request req) { static Response rootHandler(Request req) {
return Response.ok('Greetings from PassWorld!\n'); return Response.ok('Greetings from PassWorld!\n');
} }
// Check for authentication static Future<Response> getSalt(Request req) async {
static Future<Response> authenticator(Request req) async { final List<String> required = ["email"];
final List<String> required = ["email", "password"];
final body = await bodyToJson(req); final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) { if (await checkRequiredFields(required, body)) {
try { try {
await AccountsToPostgres.selectHashById(body[required[0]]); String salt =
await AccountsToPostgres.selectSaltByMail(body[required[0]]);
return Response(200, body: salt);
} catch (e) { } catch (e) {
return Response(404, return Response(204, body: 'Account already existing'); // No content
body: 'Not Found'); // no hash found -> 404 (Not Found)
} }
return Response.ok('Succesfully Authenticated'); // 200 (Ok)
} else { } else {
return Response.badRequest( return Response.badRequest(body: 'bad body');
body: 'Bad password or email !'); // 400 (Bad Request)
} }
} }
// Download sqlite password file // Check for authentication
static Response downloadPasswordDb(Request req) { static Future<Response> authenticator(Request req) async {
final mail = req.params['mail']; final List<String> required = ["email", "password"];
final password = req.params['cyphered_password_hash']; final body = await bodyToJson(req);
// Database query -> return file (List<int>) if (await checkRequiredFields(required, body)) {
// Create stream from List<int> if (await checkAuthentication(body[required[0]], body[required[1]])) {
// Rename file -> db_password_<mail>_<date> return Response.ok('Succesfully Authenticated');
// Send file } else {
return Response.unauthorized('Bad password or email !'); // 401
return Response.ok(""); }
} else {
/* return Response.badRequest(body: 'bad body'); // 401
Stream<List<int>> fileStream = file.openRead(); }
return Response.ok(fileStream, headers: {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment, filename="$reqFile"'
});
*/
} }
/*---------------|
|------POST------|
|---------------*/
// Create account // Create account
static Future<Response> createAccount(Request req) async { static Future<Response> createAccount(Request req) async {
@ -65,14 +52,14 @@ class API {
final body = await bodyToJson(req); final body = await bodyToJson(req);
if (await checkRequiredFields(required, body)) { if (await checkRequiredFields(required, body)) {
// List<String> twofa = body[required[3]];
try { try {
await AccountsToPostgres.create(body[required[0]], body[required[1]], await AccountsToPostgres.createAccount(
body[required[2]] /*, twofa*/); body[required[0]], body[required[1]], body[required[2]]);
} catch (e) { } catch (e) {
return Response(409, return Response(409,
body: 'Account already existing'); // 409 (Conflict) body: 'Account already existing'); // 409 (Conflict)
} }
print("✅ Account succesfully created");
return Response(201, return Response(201,
body: 'Account successfully created'); // 201 (Created) body: 'Account successfully created'); // 201 (Created)
} else { } else {
@ -80,38 +67,130 @@ class API {
} }
} }
/*---------------| // Delete Account
|-------PUT------| 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 // Update master password
static Response changeMasterPassword(Request req) { static Future<Response> changeMasterPassword(Request req) async {
return Response.ok("master password chnaged"); 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 // Update mail
static Response changeMail(Request req) { static Future<Response> changeMail(Request req) async {
return Response.ok("master password chnaged"); 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 // Upload sqlite password file
static Response uploadPasswordDb(Request req) { static Future<Response> uploadPasswordDb(Request req) async {
return Response.ok(""); 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)
}
} }
/*---------------| // Download sqlite password file
|-----DELETE-----| static Future<Response> downloadPasswordDb(Request req) async {
|---------------*/ final List<String> required = ["email", "password"];
final body = await bodyToJson(req);
// Delete account if (await checkRequiredFields(required, body)) {
static Response deleteAccount(Request req) { try {
return Response.ok(""); 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 // Check if required fields are in req body
static Future<bool> checkRequiredFields( static Future<bool> checkRequiredFields(
List<String> fields, Map<String, dynamic> body) async { List<String> fields, Map<String, dynamic> body) async {
@ -133,6 +212,44 @@ class API {
return json.decode(tmp); 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 // ADMIN
// //

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

Loading…
Cancel
Save