835 lines
26 KiB
Dart
835 lines
26 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:math';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:argon2/argon2.dart';
|
|
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
|
import 'package:http/http.dart' as http;
|
|
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';
|
|
|
|
SecretKey secretKey = SecretKey(Platform.environment['JOBLINK_SECRET_KEY']!);
|
|
|
|
enum BusinessType {
|
|
food,
|
|
shop,
|
|
outdoors,
|
|
manufacturing,
|
|
entertainment,
|
|
other,
|
|
}
|
|
|
|
enum JobType { cashier, server, mechanic, other }
|
|
|
|
enum OfferType { job, internship, apprenticeship }
|
|
|
|
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,
|
|
this.type,
|
|
this.website,
|
|
this.contactName,
|
|
this.contactEmail,
|
|
this.contactPhone,
|
|
this.notes,
|
|
this.locationName,
|
|
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'],
|
|
website: json['website'],
|
|
type: typeValid
|
|
? BusinessType.values.byName(json['type'])
|
|
: BusinessType.other,
|
|
contactName: json['contactName'],
|
|
contactEmail: json['contactEmail'],
|
|
contactPhone: json['contactPhone'],
|
|
notes: json['notes'],
|
|
locationName: json['locationName'],
|
|
locationAddress: json['locationAddress'],
|
|
);
|
|
}
|
|
}
|
|
|
|
class JobListing {
|
|
int? id;
|
|
int? businessId;
|
|
String name;
|
|
String description;
|
|
JobType type;
|
|
String? wage;
|
|
String? link;
|
|
OfferType offerType;
|
|
|
|
JobListing(
|
|
{this.id,
|
|
this.businessId,
|
|
required this.name,
|
|
required this.description,
|
|
required this.type,
|
|
this.wage,
|
|
this.link,
|
|
required this.offerType});
|
|
|
|
factory JobListing.fromJson(Map<String, dynamic> json) {
|
|
bool typeValid = true;
|
|
try {
|
|
JobType.values.byName(json['type']);
|
|
} catch (e) {
|
|
typeValid = false;
|
|
}
|
|
|
|
return JobListing(
|
|
id: json['id'],
|
|
businessId: json['businessId'],
|
|
name: json['name'],
|
|
description: json['description'],
|
|
type: typeValid ? JobType.values.byName(json['type']) : JobType.other,
|
|
wage: json['wage'],
|
|
link: json['link'],
|
|
offerType: OfferType.values.byName(json['offerType']));
|
|
}
|
|
}
|
|
|
|
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[0][0]);
|
|
return encoded;
|
|
}
|
|
|
|
//set defaults
|
|
String _hostname = 'localhost';
|
|
const _port = 8000;
|
|
|
|
final postgres = PostgreSQLConnection(
|
|
Platform.environment['JOBLINK_POSTGRES_ADDRESS']!,
|
|
int.parse(Platform.environment['JOBLINK_POSTGRES_PORT']!),
|
|
'fbla',
|
|
username: Platform.environment['JOBLINK_POSTGRES_USERNAME'],
|
|
password: Platform.environment['JOBLINK_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/overview/jobs', (Request request) async {
|
|
print('business overview request received');
|
|
|
|
var typeFilters = request.url.queryParameters['typeFilters']?.split(',') ??
|
|
JobType.values.asNameMap().keys;
|
|
var offerFilters =
|
|
request.url.queryParameters['offerFilters']?.split(',') ??
|
|
OfferType.values.asNameMap().keys;
|
|
|
|
var postgresResult = (await postgres.query('''
|
|
WITH business_listings AS (
|
|
SELECT b.id AS business_id,
|
|
b.name AS business_name,
|
|
b."contactName" AS business_contactName,
|
|
b."contactEmail" AS business_contactEmail,
|
|
b."contactPhone" AS business_contactPhone,
|
|
b."locationName" AS business_locationName,
|
|
b."locationAddress" AS business_locationAddress,
|
|
l.type AS listing_type,
|
|
json_agg(
|
|
json_build_object(
|
|
'id', l.id,
|
|
'businessId', l."businessId",
|
|
'name', l.name,
|
|
'description', l.description,
|
|
'type', l.type,
|
|
'offerType', l."offerType",
|
|
'wage', l.wage,
|
|
'link', l.link
|
|
)
|
|
) AS listings
|
|
FROM businesses b
|
|
JOIN listings l ON b.id = l."businessId"
|
|
WHERE l.type IN (${typeFilters.map((element) => "'$element'").join(',')})
|
|
AND l."offerType" IN (${offerFilters.map((element) => "'$element'").join(',')})
|
|
GROUP BY b.id, b.name, b."contactName", b."contactEmail", b."contactPhone",
|
|
b."locationName", b."locationAddress", l.type
|
|
),
|
|
aggregated_businesses AS (
|
|
SELECT listing_type,
|
|
json_agg(
|
|
json_build_object(
|
|
'id', business_id,
|
|
'name', business_name,
|
|
'contactName', business_contactName,
|
|
'contactEmail', business_contactEmail,
|
|
'contactPhone', business_contactPhone,
|
|
'locationName', business_locationName,
|
|
'locationAddress', business_locationAddress,
|
|
'listings', listings
|
|
)
|
|
) AS businesses
|
|
FROM business_listings
|
|
GROUP BY listing_type
|
|
)
|
|
SELECT jsonb_object_agg(listing_type, businesses) AS result
|
|
FROM aggregated_businesses;
|
|
'''));
|
|
|
|
return Response.ok(
|
|
json.encode(postgresResult[0][0]),
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
});
|
|
app.get('/fbla-api/businessdata/overview/types', (Request request) async {
|
|
print('business overview request received');
|
|
|
|
var filters = request.url.queryParameters['filters']?.split(',') ??
|
|
BusinessType.values.asNameMap().keys;
|
|
|
|
// List<Map<String, List<Map<String, dynamic>>>> this is the real type lol
|
|
Map<String, dynamic> output = {};
|
|
|
|
for (int i = 0; i < filters.length; i++) {
|
|
var postgresResult = (await postgres.query('''
|
|
SELECT json_agg(
|
|
json_build_object(
|
|
'id', id,
|
|
'name', name,
|
|
'description', description,
|
|
'website', website,
|
|
'contactEmail', "contactEmail",
|
|
'contactPhone', "contactPhone",
|
|
'locationName', "locationName",
|
|
'locationAddress', "locationAddress"
|
|
)
|
|
) FROM public.businesses WHERE type='${filters.elementAt(i)}'
|
|
'''))[0][0];
|
|
|
|
if (postgresResult != null) {
|
|
output.addAll({filters.elementAt(i): postgresResult});
|
|
}
|
|
}
|
|
|
|
// await Future.delayed(Duration(seconds: 5));
|
|
return Response.ok(
|
|
json.encode(output),
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
});
|
|
app.get('/fbla-api/businessdata/businessnames', (Request request) async {
|
|
print('business names request received');
|
|
|
|
var postgresResult = (await postgres.query('''
|
|
SELECT json_agg(
|
|
json_build_object(
|
|
'id', id,
|
|
'name', name
|
|
)
|
|
) FROM public.businesses
|
|
'''))[0][0];
|
|
|
|
return Response.ok(
|
|
json.encode(postgresResult),
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
});
|
|
app.get('/fbla-api/businessdata/business/<business>',
|
|
(Request request, String business) async {
|
|
print('idividual business data request received');
|
|
|
|
var result = (await postgres.query('''
|
|
SELECT
|
|
json_build_object(
|
|
'id', b.id,
|
|
'name', b.name,
|
|
'description', b.description,
|
|
'type', b.type,
|
|
'website', b.website,
|
|
'contactName', b."contactName",
|
|
'contactEmail', b."contactEmail",
|
|
'contactPhone', b."contactPhone",
|
|
'notes', b.notes,
|
|
'locationName', b."locationName",
|
|
'locationAddress', b."locationAddress",
|
|
'listings',
|
|
CASE
|
|
WHEN COUNT(l.id) = 0 THEN 'null'
|
|
ELSE json_agg(
|
|
json_build_object(
|
|
'id', l.id,
|
|
'businessId', l."businessId",
|
|
'name', l.name,
|
|
'description', l.description,
|
|
'type', l.type,
|
|
'offerType', l."offerType",
|
|
'wage', l.wage,
|
|
'link', l.link
|
|
)
|
|
)
|
|
END
|
|
)
|
|
FROM businesses b
|
|
LEFT JOIN listings l ON b.id = l."businessId"
|
|
WHERE b.id = $business
|
|
GROUP BY b.id;
|
|
'''))[0][0];
|
|
|
|
return Response.ok(
|
|
json.encode(result),
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
});
|
|
app.get('/fbla-api/businessdata/businesses', (Request request) async {
|
|
print('list of business data request received');
|
|
|
|
if (request.url.queryParameters['businesses'] == null) {
|
|
return Response.badRequest(
|
|
body: 'query \'businesses\' required',
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
}
|
|
var filters = request.url.queryParameters['businesses']!.split(',');
|
|
|
|
var result = (await postgres.query('''
|
|
SELECT
|
|
json_build_object(
|
|
'id', b.id,
|
|
'name', b.name,
|
|
'description', b.description,
|
|
'website', b.website,
|
|
'type', b.type,
|
|
'contactName', b."contactName",
|
|
'contactEmail', b."contactEmail",
|
|
'contactPhone', b."contactPhone",
|
|
'notes', b.notes,
|
|
'locationName', b."locationName",
|
|
'locationAddress', b."locationAddress"
|
|
)
|
|
FROM businesses b
|
|
LEFT JOIN listings l ON b.id = l."businessId"
|
|
WHERE b.id IN (${filters.join(',')})
|
|
GROUP BY b.id;
|
|
'''));
|
|
|
|
var output = result.map((element) => element[0]).toList();
|
|
|
|
return Response.ok(
|
|
json.encode(output),
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
});
|
|
app.get('/fbla-api/businessdata', (Request request) async {
|
|
print('business data request received');
|
|
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[0][0]);
|
|
return Response.ok(
|
|
encoded,
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'text/plain'
|
|
},
|
|
);
|
|
});
|
|
app.get('/fbla-api/logos/<logo>', (Request request, String logoId) {
|
|
print('business logo request received');
|
|
|
|
var logo = File('logos/$logoId.png');
|
|
try {
|
|
List<int> content = logo.readAsBytesSync();
|
|
return Response.ok(
|
|
content,
|
|
headers: {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Content-Type': 'image/png'
|
|
},
|
|
);
|
|
} catch (e) {
|
|
print('Error reading logo!');
|
|
return Response.notFound(
|
|
'logo not found',
|
|
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, website, type, "contactName", "contactPhone", "contactEmail", notes, "locationName", "locationAddress")
|
|
VALUES ('${business.name.replaceAll("'", "''")}', '${business.description.replaceAll("'", "''")}', '${business.website ?? 'NULL'}', '${business.type?.name}', '${business.contactName?.replaceAll("'", "''") ?? 'NULL'}', '${business.contactPhone ?? 'NULL'}', '${business.contactEmail ?? 'NULL'}', '${business.notes?.replaceAll("'", "''") ?? 'NULL'}', '${business.locationName?.replaceAll("'", "''") ?? 'NULL'}', '${business.locationAddress?.replaceAll("'", "''") ?? 'NULL'}')
|
|
'''
|
|
.replaceAll("'null'", 'NULL'));
|
|
|
|
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/createlisting', (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);
|
|
JobListing listing = JobListing.fromJson(json);
|
|
|
|
await postgres.query('''
|
|
INSERT INTO listings ("businessId", name, description, type, wage, link, "offerType")
|
|
VALUES ('${listing.businessId}', '${listing.name.replaceAll("'", "''")}', '${listing.description.replaceAll("'", "''")}', '${listing.type.name}', '${listing.wage ?? 'NULL'}', '${listing.link?.replaceAll("'", "''") ?? 'NULL'}', '${listing.offerType.name}')
|
|
'''
|
|
.replaceAll("'null'", 'NULL'));
|
|
|
|
final dbListing = await postgres.query('''SELECT id FROM public.listings
|
|
ORDER BY id DESC LIMIT 1''');
|
|
var id = dbListing[0][0];
|
|
|
|
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=$id;');
|
|
|
|
try {
|
|
await File('logos/$id.png').delete();
|
|
} catch (e) {
|
|
print('Failure to delete logo! $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/deletelisting', (Request request) async {
|
|
print('delete listing 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.listings WHERE id=$id;');
|
|
|
|
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("'", "''").replaceAll("\"", "\"\"")}'::text, description = '${business.description.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text, website = '${business.website!}'::text, "contactName" = '${business.contactName!.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text, "contactPhone" = '${business.contactPhone!}'::text, "contactEmail" = '${business.contactEmail!}'::text, notes = '${business.notes!.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text, "locationName" = '${business.locationName!.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text, "locationAddress" = '${business.locationAddress!.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text WHERE
|
|
id = ${business.id};
|
|
'''
|
|
.replaceAll("'null'", 'NULL'));
|
|
|
|
var logoResponse = await http.get(
|
|
Uri.http('logo.clearbit.com', '/${business.website}'),
|
|
);
|
|
|
|
try {
|
|
await File('logos/${business.id}.png').delete();
|
|
} catch (e) {
|
|
print('Failure to delete logo! $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/editlisting', (Request request) async {
|
|
print('edit listing request received');
|
|
|
|
final payload = await request.readAsString();
|
|
var auth = request.headers['Authorization']?.replaceAll('Bearer ', '');
|
|
try {
|
|
JWT.verify(auth!, secretKey);
|
|
|
|
var json = jsonDecode(payload);
|
|
JobListing listing = JobListing.fromJson(json);
|
|
|
|
await postgres.query('''
|
|
UPDATE listings SET
|
|
"businessId" = ${listing.businessId}, name = '${listing.name.replaceAll("'", "''")}'::text, description = '${listing.description.replaceAll("'", "''")}'::text, type = '${listing.type.name}'::text, wage = '${listing.wage ?? 'NULL'}'::text, link = '${listing.link?.replaceAll("'", "''") ?? 'NULL'}'::text, "offerType"='${listing.offerType.name}'::text WHERE
|
|
id = ${listing.id};
|
|
'''
|
|
.replaceAll("'null'", 'NULL'));
|
|
|
|
return Response.ok(
|
|
listing.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}');
|
|
}
|