Compare commits

..

No commits in common. "9116876f7bde88f8f8c1c17af657207ece7c9c0f" and "68edb2c3a120619a37857803eecb78f192d2091c" have entirely different histories.

4 changed files with 45 additions and 291 deletions

View File

@ -3,7 +3,6 @@ version: '3'
services: services:
fbla_api: fbla_api:
image: fbla-api image: fbla-api
restart: unless-stopped
ports: ports:
- "8000:8000" - "8000:8000"
volumes: volumes:

View File

@ -22,37 +22,47 @@ enum BusinessType {
other, other,
} }
enum JobType { cashier, server, mechanic, other }
class Business { class Business {
int id; int id;
String name; String name;
String description; String description;
String? website; BusinessType type;
String? contactName; String website;
String? contactEmail; String contactName;
String? contactPhone; String contactEmail;
String? notes; String contactPhone;
String? locationName; String notes;
String? locationAddress; String locationName;
String locationAddress;
Business( Business({
{required this.id, required this.id,
required this.name, required this.name,
required this.description, required this.description,
this.website, required this.type,
this.contactName, required this.website,
this.contactEmail, required this.contactName,
this.contactPhone, required this.contactEmail,
this.notes, required this.contactPhone,
this.locationName, required this.notes,
this.locationAddress}); required this.locationName,
required this.locationAddress,
});
factory Business.fromJson(Map<String, dynamic> json) { factory Business.fromJson(Map<String, dynamic> json) {
bool typeValid = true;
try {
BusinessType.values.byName(json['type']);
} catch (e) {
typeValid = false;
}
return Business( return Business(
id: json['id'], id: json['id'],
name: json['name'], name: json['name'],
description: json['description'], description: json['description'],
type: typeValid
? BusinessType.values.byName(json['type'])
: BusinessType.other,
website: json['website'], website: json['website'],
contactName: json['contactName'], contactName: json['contactName'],
contactEmail: json['contactEmail'], contactEmail: json['contactEmail'],
@ -64,44 +74,6 @@ class Business {
} }
} }
class JobListing {
String? id;
String? businessId;
String name;
String description;
JobType type;
String? wage;
String? link;
JobListing(
{this.id,
this.businessId,
required this.name,
required this.description,
required this.type,
this.wage,
this.link});
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'],
);
}
}
Future<String> fetchBusinessData() async { Future<String> fetchBusinessData() async {
final result = await postgres.query(''' final result = await postgres.query('''
SELECT json_agg( SELECT json_agg(
@ -121,7 +93,9 @@ Future<String> fetchBusinessData() async {
) FROM businesses ) FROM businesses
'''); ''');
var encoded = json.encode(result[0][0]); var encoded = json.encode(result);
var decoded = json.decode(encoded);
encoded = json.encode(decoded[0][0]);
return encoded; return encoded;
} }
@ -151,95 +125,12 @@ void main() async {
headers: {'Access-Control-Allow-Origin': '*'}, headers: {'Access-Control-Allow-Origin': '*'},
); );
}); });
app.get('/fbla-api/businessdata/overview', (Request request) async {
print('business overview request received');
var filters = request.url.queryParameters['filters']?.split(',') ??
JobType.values.asNameMap().keys;
// List<Map<String, List<Map<String, dynamic>>>> this is the real type lol
List<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"
)
) FROM public.businesses WHERE id IN (SELECT "businessId" FROM public.listings WHERE type='${filters.elementAt(i)}')
'''))[0][0];
if (postgresResult != null) {
output.add({filters.elementAt(i): postgresResult});
}
}
return Response.ok(
json.encode(output),
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,
'website', b.website,
'contactName', b."contactName",
'contactEmail', b."contactEmail",
'contactPhone', b."contactPhone",
'notes', b.notes,
'locationName', b."locationName",
'locationAddress', b."locationAddress",
'listings',
json_agg(
json_build_object(
'id', l.id,
'name', l.name,
'description', l.description,
'type', l.type,
'wage', l.wage,
'link', l.link
)
)
)
FROM businesses b
LEFT JOIN listings l ON b.id = l.business_id
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', (Request request) async { app.get('/fbla-api/businessdata', (Request request) async {
print('business data request received'); print('business data request received');
final output = await fetchBusinessData(); final output = await fetchBusinessData();
return Response.ok( return Response.ok(
output.toString(), output.toString(),
headers: { headers: {'Access-Control-Allow-Origin': '*'},
'Access-Control-Allow-Origin': '*',
'Content-Type': 'text/plain'
},
); );
}); });
app.get('/fbla-api/logos/<logo>', (Request request, String logoId) { app.get('/fbla-api/logos/<logo>', (Request request, String logoId) {
@ -267,8 +158,8 @@ void main() async {
Business business = Business.fromJson(json); Business business = Business.fromJson(json);
await postgres.query(''' await postgres.query('''
INSERT INTO businesses (name, description, website, "contactName", "contactPhone", "contactEmail", notes, "locationName", "locationAddress") INSERT INTO businesses (name, description, type, website, "contactName", "contactPhone", "contactEmail", notes, "locationName", "locationAddress")
VALUES ('${business.name.replaceAll("'", "''")}', '${business.description.replaceAll("'", "''")}', '${business.website ?? 'NULL'}', '${business.contactName?.replaceAll("'", "''") ?? 'NULL'}', '${business.contactPhone ?? 'NULL'}', '${business.contactEmail ?? 'NULL'}', '${business.notes?.replaceAll("'", "''") ?? 'NULL'}', '${business.locationName?.replaceAll("'", "''") ?? 'NULL'}', '${business.locationAddress?.replaceAll("'", "''") ?? 'NULL'}') 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 final dbBusiness = await postgres.query('''SELECT * FROM public.businesses
@ -296,40 +187,6 @@ void main() async {
headers: {'Access-Control-Allow-Origin': '*'}, 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)
VALUES ('${listing.businessId}' '${listing.name.replaceAll("'", "''")}', '${listing.description.replaceAll("'", "''")}', '${listing.type.name}', '${listing.wage ?? 'NULL'}', '${listing.link?.replaceAll("'", "''") ?? '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 { app.post('/fbla-api/deletebusiness', (Request request) async {
print('delete business request received'); print('delete business request received');
@ -340,7 +197,11 @@ void main() async {
var json = jsonDecode(payload); var json = jsonDecode(payload);
var id = json['id']; var id = json['id'];
await postgres.query('DELETE FROM public.business WHERE id=$id;'); await postgres.query('''
DELETE FROM public.businesses
WHERE id IN
($id);
''');
try { try {
await File('logos/$id.png').delete(); await File('logos/$id.png').delete();
@ -363,33 +224,6 @@ void main() async {
headers: {'Access-Control-Allow-Origin': '*'}, 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 { app.post('/fbla-api/editbusiness', (Request request) async {
print('edit business request received'); print('edit business request received');
@ -403,7 +237,7 @@ void main() async {
await postgres.query(''' await postgres.query('''
UPDATE businesses SET 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 name = '${business.name.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text, description = '${business.description.replaceAll("'", "''").replaceAll("\"", "\"\"")}'::text, website = '${business.website}'::text, type = '${business.type.name}'::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}; id = ${business.id};
'''); ''');
@ -436,38 +270,6 @@ void main() async {
headers: {'Access-Control-Allow-Origin': '*'}, 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 WHERE
id = ${listing.id};
''');
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 { app.post('/fbla-api/signin', (Request request) async {
print('signin request received'); print('signin request received');

View File

@ -81,6 +81,7 @@ void main() async {
"id": 0, "id": 0,
"name": "tmp", "name": "tmp",
"description": "tmp", "description": "tmp",
"type": "business",
"website": "tmp", "website": "tmp",
"contactName": "tmp", "contactName": "tmp",
"contactEmail": "tmp", "contactEmail": "tmp",

View File

@ -26,7 +26,6 @@ class _CreateBusinessDetailState extends State<BusinessDetail> {
), ),
body: ListView( body: ListView(
children: [ children: [
// Title, logo, desc, website
Card( Card(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Column( child: Column(
@ -42,7 +41,8 @@ class _CreateBusinessDetailState extends State<BusinessDetail> {
), ),
leading: ClipRRect( leading: ClipRRect(
borderRadius: BorderRadius.circular(6.0), borderRadius: BorderRadius.circular(6.0),
child: Image.network('$apiAddress/logos/${business.id}', child: Image.network(
'$apiAddress/logos/${business.id}',
width: 48, width: 48,
height: 48, errorBuilder: (BuildContext context, height: 48, errorBuilder: (BuildContext context,
Object exception, StackTrace? stackTrace) { Object exception, StackTrace? stackTrace) {
@ -63,52 +63,6 @@ class _CreateBusinessDetailState extends State<BusinessDetail> {
], ],
), ),
), ),
// Available positions
Card(
child: Padding(
padding: const EdgeInsets.only(left: 16.0, top: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Available Postitions',
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
// Container(
// height: 400,
// width: 300,
ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
ListTile(
title: Text('Postition 1'),
leading: Icon(Icons.work),
onTap: () {
// launchUrl(Uri.parse(''));
},
),
ListTile(
title: Text('Postition 2'),
leading: Icon(Icons.work),
onTap: () {
// launchUrl(Uri.parse(''));
},
),
ListTile(
title: Text('Postition 3'),
leading: Icon(Icons.work),
onTap: () {
// launchUrl(Uri.parse(''));
},
),
],
),
]),
),
),
// Contact info
Visibility( Visibility(
visible: (business.contactEmail.isNotEmpty || visible: (business.contactEmail.isNotEmpty ||
business.contactPhone.isNotEmpty), business.contactPhone.isNotEmpty),
@ -184,7 +138,6 @@ class _CreateBusinessDetailState extends State<BusinessDetail> {
), ),
), ),
), ),
// Location
Visibility( Visibility(
child: Card( child: Card(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
@ -199,7 +152,6 @@ class _CreateBusinessDetailState extends State<BusinessDetail> {
), ),
), ),
), ),
// Notes
Visibility( Visibility(
visible: business.notes.isNotEmpty, visible: business.notes.isNotEmpty,
child: Card( child: Card(