init commit - move from separate git repos
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'package:argon2/argon2.dart';
|
||||
import 'dart:io';
|
||||
import 'package:postgres/postgres.dart';
|
||||
|
||||
String username = 'admin';
|
||||
String password = 'password';
|
||||
|
||||
var r = Random.secure();
|
||||
String randomSalt = String.fromCharCodes(List.generate(32, (index) => r.nextInt(33) + 89));
|
||||
final salt = randomSalt.toBytesLatin1();
|
||||
|
||||
var parameters = Argon2Parameters(
|
||||
Argon2Parameters.ARGON2_i,
|
||||
salt,
|
||||
version: Argon2Parameters.ARGON2_VERSION_10,
|
||||
iterations: 2,
|
||||
memoryPowerOf2: 16,
|
||||
);
|
||||
|
||||
final postgres = PostgreSQLConnection(
|
||||
Platform.environment['POSTGRES_ADDRESS']!,
|
||||
int.parse(Platform.environment['POSTGRES_PORT']!),
|
||||
'fbla',
|
||||
username: Platform.environment['POSTGRES_USERNAME'],
|
||||
password: Platform.environment['POSTGRES_PASSWORD'],
|
||||
);
|
||||
|
||||
Future<void> main() async {
|
||||
await postgres.open();
|
||||
var argon2 = Argon2BytesGenerator();
|
||||
argon2.init(parameters);
|
||||
|
||||
var passwordBytes = parameters.converter.convert(password);
|
||||
var result = Uint8List(32);
|
||||
argon2.generateBytes(passwordBytes, result);
|
||||
var resultHex = result.toHexString();
|
||||
|
||||
postgres.query(
|
||||
'''
|
||||
INSERT INTO public.users (username, password_hash, salt)
|
||||
VALUES ('$username', '$resultHex', '$randomSalt')
|
||||
'''
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
import 'package:postgres/postgres.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf/shelf_io.dart' as io;
|
||||
import 'package:shelf_router/shelf_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:argon2/argon2.dart';
|
||||
|
||||
|
||||
SecretKey secretKey = SecretKey(Platform.environment['SECRET_KEY']!);
|
||||
|
||||
enum BusinessType {
|
||||
food,
|
||||
shop,
|
||||
outdoors,
|
||||
manufacturing,
|
||||
other,
|
||||
}
|
||||
|
||||
class Business {
|
||||
int id;
|
||||
String name;
|
||||
String description;
|
||||
BusinessType type;
|
||||
String website;
|
||||
String contactName;
|
||||
String contactEmail;
|
||||
String contactPhone;
|
||||
String notes;
|
||||
String locationName;
|
||||
String locationAddress;
|
||||
|
||||
Business({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.type,
|
||||
required this.website,
|
||||
required this.contactName,
|
||||
required this.contactEmail,
|
||||
required this.contactPhone,
|
||||
required this.notes,
|
||||
required this.locationName,
|
||||
required this.locationAddress,
|
||||
});
|
||||
|
||||
factory Business.fromJson(Map<String, dynamic> json) {
|
||||
bool typeValid = true;
|
||||
try {
|
||||
BusinessType.values.byName(json['type']);
|
||||
} catch (e) {
|
||||
typeValid = false;
|
||||
}
|
||||
return Business(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
description: json['description'],
|
||||
type: typeValid
|
||||
? BusinessType.values.byName(json['type'])
|
||||
: BusinessType.other,
|
||||
website: json['website'],
|
||||
contactName: json['contactName'],
|
||||
contactEmail: json['contactEmail'],
|
||||
contactPhone: json['contactPhone'],
|
||||
notes: json['notes'],
|
||||
locationName: json['locationName'],
|
||||
locationAddress: json['locationAddress'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> fetchBusinessData() async {
|
||||
final result = await postgres.query(
|
||||
'''
|
||||
SELECT json_agg(
|
||||
json_build_object(
|
||||
'id', id,
|
||||
'name', name,
|
||||
'description', description,
|
||||
'type', type,
|
||||
'website', website,
|
||||
'contactName', "contactName",
|
||||
'contactEmail', "contactEmail",
|
||||
'contactPhone', "contactPhone",
|
||||
'notes', notes,
|
||||
'locationName', "locationName",
|
||||
'locationAddress', "locationAddress"
|
||||
)
|
||||
) FROM businesses
|
||||
'''
|
||||
);
|
||||
|
||||
var encoded = json.encode(result);
|
||||
var decoded = json.decode(encoded);
|
||||
encoded = json.encode(decoded[0][0]);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
//set defaults
|
||||
String _hostname = 'localhost';
|
||||
const _port = 8000;
|
||||
|
||||
|
||||
final postgres = PostgreSQLConnection(
|
||||
Platform.environment['POSTGRES_ADDRESS']!,
|
||||
int.parse(Platform.environment['POSTGRES_PORT']!),
|
||||
'fbla',
|
||||
username: Platform.environment['POSTGRES_USERNAME'],
|
||||
password: Platform.environment['POSTGRES_PASSWORD'],
|
||||
);
|
||||
|
||||
|
||||
|
||||
void main() async {
|
||||
await postgres.open();
|
||||
|
||||
final app = Router();
|
||||
|
||||
// routes
|
||||
app.get('/fbla-api/hello', (Request request) async {
|
||||
print('Hello received');
|
||||
|
||||
return Response.ok(
|
||||
'Hello, World!',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.get('/fbla-api/businessdata', (Request request) async {
|
||||
print('business data request received');
|
||||
final output = await fetchBusinessData();
|
||||
return Response.ok(
|
||||
output.toString(),
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.get('/fbla-api/logos/<logo>', (Request request, String logoId) {
|
||||
print('business logo request received');
|
||||
|
||||
var logo = File('logos/$logoId.png');
|
||||
List<int> content = logo.readAsBytesSync();
|
||||
|
||||
return Response.ok(
|
||||
content,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'image/png'
|
||||
},
|
||||
);
|
||||
});
|
||||
app.post('/fbla-api/createbusiness', (Request request) async {
|
||||
print('create business request received');
|
||||
|
||||
final payload = await request.readAsString();
|
||||
var auth = request.headers['Authorization']?.replaceAll('Bearer ', '');
|
||||
try {
|
||||
JWT.verify(auth!, secretKey);
|
||||
var json = jsonDecode(payload);
|
||||
Business business = Business.fromJson(json);
|
||||
|
||||
await postgres.query(
|
||||
'''
|
||||
INSERT INTO businesses (name, description, type, website, "contactName", "contactPhone", "contactEmail", notes, "locationName", "locationAddress")
|
||||
VALUES ('${business.name.replaceAll("'", "''")}', '${business.description.replaceAll("'", "''")}', '${business.type.name}', '${business.website}', '${business.contactName.replaceAll("'", "''")}', '${business.contactPhone}', '${business.contactEmail}', '${business.notes.replaceAll("'", "''")}', '${business.locationName.replaceAll("'", "''")}', '${business.locationAddress.replaceAll("'", "''")}')
|
||||
'''
|
||||
);
|
||||
|
||||
final dbBusiness = await postgres.query(
|
||||
'''SELECT * FROM public.businesses
|
||||
ORDER BY id DESC LIMIT 1''');
|
||||
var id = dbBusiness[0][0];
|
||||
var logoResponse = await http.get(
|
||||
Uri.http('logo.clearbit.com', '/${business.website}'),
|
||||
);
|
||||
|
||||
if (logoResponse.headers.toString().contains('image/png')) {
|
||||
await File('logos/$id.png').writeAsBytes(logoResponse.bodyBytes);
|
||||
}
|
||||
return Response.ok(
|
||||
id.toString(),
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
} on JWTExpiredException {
|
||||
print('JWT Expired');
|
||||
} on JWTException catch (e) {
|
||||
print(e.message);
|
||||
}
|
||||
|
||||
return Response.unauthorized(
|
||||
'unauthorized',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.post('/fbla-api/deletebusiness', (Request request) async {
|
||||
print('delete business request received');
|
||||
|
||||
|
||||
final payload = await request.readAsString();
|
||||
var auth = request.headers['Authorization']?.replaceAll('Bearer ', '');
|
||||
try {
|
||||
JWT.verify(auth!, secretKey);
|
||||
var json = jsonDecode(payload);
|
||||
var id = json['id'];
|
||||
|
||||
await postgres.query(
|
||||
'''
|
||||
DELETE FROM public.businesses
|
||||
WHERE id IN
|
||||
($id);
|
||||
'''
|
||||
);
|
||||
|
||||
try{
|
||||
await File('logos/$id.png').delete();
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
return Response.ok(
|
||||
id.toString(),
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
} on JWTExpiredException {
|
||||
print('JWT Expired');
|
||||
} on JWTException catch (e) {
|
||||
print(e.message);
|
||||
}
|
||||
|
||||
return Response.unauthorized(
|
||||
'unauthorized',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.post('/fbla-api/editbusiness', (Request request) async {
|
||||
print('edit business request received');
|
||||
|
||||
final payload = await request.readAsString();
|
||||
var auth = request.headers['Authorization']?.replaceAll('Bearer ', '');
|
||||
try {
|
||||
JWT.verify(auth!, secretKey);
|
||||
|
||||
var json = jsonDecode(payload);
|
||||
Business business = Business.fromJson(json);
|
||||
|
||||
await postgres.query(
|
||||
'''
|
||||
UPDATE businesses SET
|
||||
name = '${business.name.replaceAll("'", "''")}'::text, description = '${business.description.replaceAll("'", "''")}'::text, website = '${business.website}'::text, type = '${business.type.name}'::text, "contactName" = '${business.contactName.replaceAll("'", "''")}'::text, "contactPhone" = '${business.contactPhone}'::text, "contactEmail" = '${business.contactEmail}'::text, notes = '${business.notes.replaceAll("'", "''")}'::text, "locationName" = '${business.locationName.replaceAll("'", "''")}'::text, "locationAddress" = '${business.locationAddress.replaceAll("'", "''")}'::text WHERE
|
||||
id = ${business.id};
|
||||
'''
|
||||
);
|
||||
|
||||
var logoResponse = await http.get(
|
||||
Uri.http('logo.clearbit.com', '/${business.website}'),
|
||||
);
|
||||
|
||||
try {
|
||||
await File('logos/${business.id}.png').delete();
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
if (logoResponse.headers.toString().contains('image/png')) {
|
||||
await File('logos/${business.id}.png').writeAsBytes(logoResponse.bodyBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Response.ok(
|
||||
business.id.toString(),
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
} on JWTExpiredException {
|
||||
print('JWT Expired');
|
||||
} on JWTException catch (e) {
|
||||
print(e.message);
|
||||
}
|
||||
|
||||
return Response.unauthorized(
|
||||
'unauthorized',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.post('/fbla-api/signin', (Request request) async {
|
||||
print('signin request received');
|
||||
|
||||
final payload = await request.readAsString();
|
||||
var json = jsonDecode(payload);
|
||||
var username = json['username'];
|
||||
var password = json['password'];
|
||||
|
||||
var saltDb = await postgres.query(
|
||||
'SELECT salt FROM users WHERE username=\'$username\''
|
||||
);
|
||||
if (saltDb.isEmpty) {
|
||||
return Response.unauthorized(
|
||||
'invalid username',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
}
|
||||
|
||||
var saltString = saltDb[0][0].toString();
|
||||
var salt = saltString.toBytesLatin1();
|
||||
|
||||
var parameters = Argon2Parameters(
|
||||
Argon2Parameters.ARGON2_i,
|
||||
salt,
|
||||
version: Argon2Parameters.ARGON2_VERSION_10,
|
||||
iterations: 2,
|
||||
memoryPowerOf2: 16,
|
||||
);
|
||||
var argon2 = Argon2BytesGenerator();
|
||||
argon2.init(parameters);
|
||||
|
||||
var passwordBytes = parameters.converter.convert(password);
|
||||
var result = Uint8List(32);
|
||||
argon2.generateBytes(passwordBytes, result);
|
||||
var resultHex = result.toHexString();
|
||||
|
||||
var passwordHashDb = await postgres.query(
|
||||
'SELECT password_hash FROM users WHERE username=\'$username\''
|
||||
);
|
||||
var passwordHash = passwordHashDb[0][0].toString();
|
||||
|
||||
if (passwordHash == resultHex) {
|
||||
|
||||
final jwt = JWT(
|
||||
{
|
||||
'username': username
|
||||
},
|
||||
);
|
||||
final token = jwt.sign(secretKey);
|
||||
try {
|
||||
JWT.verify(token, secretKey);
|
||||
} on JWTExpiredException {
|
||||
print('JWT Expired');
|
||||
} on JWTException catch (e) {
|
||||
print(e.message);
|
||||
}
|
||||
|
||||
return Response.ok(
|
||||
token.toString(),
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
|
||||
} else {
|
||||
return Response.unauthorized(
|
||||
'invalid password',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
}
|
||||
});
|
||||
app.post('/fbla-api/createuser', (Request request) async {
|
||||
print('create user request received');
|
||||
|
||||
var auth = request.headers['Authorization']?.replaceAll('Bearer ', '');
|
||||
final payload = await request.readAsString();
|
||||
|
||||
try {
|
||||
JWT.verify(auth!, secretKey);
|
||||
|
||||
var json = jsonDecode(payload);
|
||||
var username = json['username'];
|
||||
var password = json['password'];
|
||||
|
||||
var r = Random.secure();
|
||||
String randomSalt = String.fromCharCodes(List.generate(32, (index) => r.nextInt(33) + 89));
|
||||
final salt = randomSalt.toBytesLatin1();
|
||||
|
||||
var parameters = Argon2Parameters(
|
||||
Argon2Parameters.ARGON2_i,
|
||||
salt,
|
||||
version: Argon2Parameters.ARGON2_VERSION_10,
|
||||
iterations: 2,
|
||||
memoryPowerOf2: 16,
|
||||
);
|
||||
var argon2 = Argon2BytesGenerator();
|
||||
argon2.init(parameters);
|
||||
|
||||
var passwordBytes = parameters.converter.convert(password);
|
||||
var result = Uint8List(32);
|
||||
argon2.generateBytes(passwordBytes, result);
|
||||
var resultHex = result.toHexString();
|
||||
|
||||
postgres.query(
|
||||
'''
|
||||
INSERT INTO public.users (username, password_hash, salt)
|
||||
VALUES ('$username', '$resultHex', '$randomSalt')
|
||||
'''
|
||||
);
|
||||
|
||||
return Response.ok(
|
||||
username,
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
|
||||
} on JWTExpiredException {
|
||||
print('JWT Expired');
|
||||
} on JWTException catch (e) {
|
||||
print(e.message);
|
||||
}
|
||||
|
||||
return Response.unauthorized(
|
||||
'unauthorized',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.post('/fbla-api/deleteuser', (Request request) async {
|
||||
print('delete user request received');
|
||||
|
||||
var auth = request.headers['Authorization']?.replaceAll('Bearer ', '');
|
||||
final payload = await request.readAsString();
|
||||
|
||||
try {
|
||||
JWT.verify(auth!, secretKey);
|
||||
|
||||
var json = jsonDecode(payload);
|
||||
var username = json['username'];
|
||||
|
||||
postgres.query(
|
||||
'''
|
||||
DELETE FROM public.users
|
||||
WHERE username IN ('$username');
|
||||
'''
|
||||
);
|
||||
|
||||
return Response.ok(
|
||||
username,
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
|
||||
} on JWTExpiredException {
|
||||
print('JWT Expired');
|
||||
} on JWTException catch (e) {
|
||||
print(e.message);
|
||||
}
|
||||
|
||||
return Response.unauthorized(
|
||||
'unauthorized',
|
||||
headers: {'Access-Control-Allow-Origin': '*'},
|
||||
);
|
||||
});
|
||||
app.get('/fbla-api/marinodev', (Request request) async {
|
||||
print('marinodev request received');
|
||||
|
||||
var logo = File('MarinoDev.svg');
|
||||
List<int> content = logo.readAsBytesSync();
|
||||
|
||||
return Response.ok(
|
||||
content,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'image/svg+xml'
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// get ip address for hosting
|
||||
for (var interface in await NetworkInterface.list()) {
|
||||
for (var addr in interface.addresses) {
|
||||
if (addr.type == InternetAddressType.IPv4) {
|
||||
_hostname = addr.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final server = await io.serve(app, _hostname, _port);
|
||||
|
||||
print('Serving at http://${server.address.host}:${server.port}');
|
||||
}
|
||||
Reference in New Issue
Block a user