Compare commits

..

No commits in common. "master" and "openai-implementation" have entirely different histories.

27 changed files with 111 additions and 324 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ API/bin
API/obj API/obj
.idea .idea
/rust-backend/.env.example

View File

@ -1,32 +0,0 @@
using API.Models;
using API.Persistence.Repositories;
using Microsoft.AspNetCore.Mvc;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage;
using Newtonsoft.Json.Linq;
namespace API.Application.Users.Queries
{
public class QueryUsersByIds
{
private readonly IUserRepository _repository;
public QueryUsersByIds(IUserRepository repository)
{
_repository = repository;
}
public async Task<ActionResult<List<UserDTO>>> Handle(List<string> ids)
{
List<User> users = await _repository.QueryUsersByIdsAsync(ids);
if (users == null)
{
return new ConflictObjectResult(new { message = "No user on given Id" });
}
return new OkObjectResult(users.Select(user => new { id = user.Id, email = user.Email, username = user.Username, profilePicture = user.ProfilePicture, createdAt = user.CreatedAt }));
}
}
}

View File

@ -22,18 +22,18 @@ namespace API.Controllers
{ {
private readonly QueryAllUsers _queryAllUsers; private readonly QueryAllUsers _queryAllUsers;
private readonly QueryUserById _queryUserById; private readonly QueryUserById _queryUserById;
private readonly QueryUsersByIds _queryUsersByIds;
private readonly CreateUser _createUser; private readonly CreateUser _createUser;
private readonly UpdateUser _updateUser; private readonly UpdateUser _updateUser;
private readonly DeleteUser _deleteUser; private readonly DeleteUser _deleteUser;
private readonly LoginUser _loginUser; private readonly LoginUser _loginUser;
private readonly TokenHelper _tokenHelper; private readonly TokenHelper _tokenHelper;
private readonly IUserRepository _repository; private readonly IUserRepository _repository;
public UsersController( public UsersController(
QueryAllUsers queryAllUsers, QueryAllUsers queryAllUsers,
QueryUserById queryUserById, QueryUserById queryUserById,
QueryUsersByIds queryUsersByIds,
CreateUser createUser, CreateUser createUser,
UpdateUser updateUser, UpdateUser updateUser,
DeleteUser deleteUser, DeleteUser deleteUser,
@ -44,13 +44,14 @@ namespace API.Controllers
{ {
_queryAllUsers = queryAllUsers; _queryAllUsers = queryAllUsers;
_queryUserById = queryUserById; _queryUserById = queryUserById;
_queryUsersByIds = queryUsersByIds;
_createUser = createUser; _createUser = createUser;
_updateUser = updateUser; _updateUser = updateUser;
_deleteUser = deleteUser; _deleteUser = deleteUser;
_loginUser = loginUser; _loginUser = loginUser;
_tokenHelper = tokenHelper; _tokenHelper = tokenHelper;
_repository = repository; _repository = repository;
} }
[HttpPost("login")] [HttpPost("login")]
@ -72,16 +73,9 @@ namespace API.Controllers
return await _queryUserById.Handle(id); return await _queryUserById.Handle(id);
} }
[HttpGet("UsersByIds")]
public async Task<ActionResult<List<UserDTO>>> GetUsersByIds(string userIds)
{
List<string> ids = userIds.Split(",").ToList();
return await _queryUsersByIds.Handle(ids);
}
[Authorize] [Authorize]
[HttpPut] [HttpPut]
public async Task<IActionResult> PutUser([FromForm] UpdateUserDTO UpdateUserDTO) public async Task<IActionResult> PutUser([FromForm ]UpdateUserDTO UpdateUserDTO)
{ {
return await _updateUser.Handle(UpdateUserDTO); return await _updateUser.Handle(UpdateUserDTO);
} }

View File

@ -8,7 +8,6 @@ namespace API.Persistence.Repositories
Task<bool> DeleteUserAsync(string id); Task<bool> DeleteUserAsync(string id);
Task<List<User>> QueryAllUsersAsync(); Task<List<User>> QueryAllUsersAsync();
Task<User> QueryUserByIdAsync(string id); Task<User> QueryUserByIdAsync(string id);
Task<List<User>> QueryUsersByIdsAsync(List<string> ids);
Task<User> QueryUserByEmailAsync(string email); Task<User> QueryUserByEmailAsync(string email);
Task<bool> UpdateUserAsync(User user); Task<bool> UpdateUserAsync(User user);
Task<User> QueryUserByRefreshTokenAsync(string refreshToken); Task<User> QueryUserByRefreshTokenAsync(string refreshToken);

View File

@ -1,6 +1,5 @@
using API.Models; using API.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages; using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
namespace API.Persistence.Repositories namespace API.Persistence.Repositories
@ -26,18 +25,6 @@ namespace API.Persistence.Repositories
} }
} }
public async Task<List<User>> QueryUsersByIdsAsync(List<string> ids)
{
try
{
return _context.Users.Where(user => ids.Contains(user.Id)).ToList();
}
catch (Exception)
{
return [];
}
}
public async Task<string> CreateUserAsync(User user) public async Task<string> CreateUserAsync(User user)
{ {
try try

View File

@ -43,7 +43,7 @@ namespace API
builder.Services.AddScoped<DeleteUser>(); builder.Services.AddScoped<DeleteUser>();
builder.Services.AddScoped<LoginUser>(); builder.Services.AddScoped<LoginUser>();
builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<QueryUsersByIds>();
IConfiguration Configuration = builder.Configuration; IConfiguration Configuration = builder.Configuration;

View File

@ -8,7 +8,6 @@ Environment=PATH=$PATH:/home/reimar/.dotnet
Environment=DEFAULT_CONNECTION="Data Source=/home/reimar/skantravels/database.sqlite3" Environment=DEFAULT_CONNECTION="Data Source=/home/reimar/skantravels/database.sqlite3"
ExecStartPre=/home/reimar/skantravels/efbundle ExecStartPre=/home/reimar/skantravels/efbundle
ExecStart=/home/reimar/skantravels/API --urls=http://0.0.0.0:5001 ExecStart=/home/reimar/skantravels/API --urls=http://0.0.0.0:5001
WorkingDirectory=/home/reimar/skantravels
Type=simple Type=simple
[Install] [Install]

View File

@ -151,7 +151,7 @@ KC - Kundechef
## Torsdag ## Torsdag
**SM(Alexander):** Samarbejdede med Reimar omkring Refresh-tokens, mest med ide-fasen **SM(Alexander):**
**PO(Reimar):** Begyndt på implementering af refresh tokens, fikset en fejl med en openssl dependency til vores release-flow **PO(Reimar):** Begyndt på implementering af refresh tokens, fikset en fejl med en openssl dependency til vores release-flow
@ -159,7 +159,7 @@ KC - Kundechef
## Fredag ## Fredag
**SM(Alexander):** Holdt kundemøder **SM(Alexander):**
**PO(Reimar):** Holdt kundemøder **PO(Reimar):** Holdt kundemøder
@ -177,7 +177,7 @@ KC - Kundechef
## Tirsdag ## Tirsdag
**PO(Alexander):** Pair-programmede med Reimar om nedenstående features **PO(Alexander):**
**KC(Reimar):** Implementeret reviews i rust backenden, fået vist dem som location pins på kortet **KC(Reimar):** Implementeret reviews i rust backenden, fået vist dem som location pins på kortet
@ -185,7 +185,7 @@ KC - Kundechef
## Onsdag ## Onsdag
**PO(Alexander):** Pair-programmede med Reimar om nedenstående features **PO(Alexander):**
**KC(Reimar):** Implementeret review-liste når man klikker ind på én **KC(Reimar):** Implementeret review-liste når man klikker ind på én
@ -193,7 +193,7 @@ KC - Kundechef
## Torsdag ## Torsdag
**PO(Alexander):** Pair-programmede med Reimar om nedenstående features **PO(Alexander):**
**KC(Reimar):** Lavet create review-side, tilføjet stjerner til review-liste **KC(Reimar):** Lavet create review-side, tilføjet stjerner til review-liste
@ -201,7 +201,7 @@ KC - Kundechef
## Fredag ## Fredag
**PO(Alexander):** Hjalp med kundemøder **PO(Alexander):**
**KC(Reimar):** Holdt kundemøder **KC(Reimar):** Holdt kundemøder
@ -211,59 +211,49 @@ KC - Kundechef
## Mandag ## Mandag
**KC(Alexander):** Aftalte udviklermøde **KC(Alexander):**
**SM(Reimar):** Påbegyndt implementation af AWS image upload i Rust **SM(Reimar):**
**PO(Philip):** Fik billede upload til at virke efter reimar sagde at jeg skulle sende fra flutter som en form i stedet for JSON **PO(Philip):**
## Tirsdag ## Tirsdag
**KC(Alexander):** **KC(Alexander):**
**SM(Reimar):** Færdiggjort image uploads i Rust **SM(Reimar):**
**PO(Philip):** intet med kundegruppen. Fik billede til at vise icon hvis der ikke var billede. og det nye billede blev vist efter man havde savet **PO(Philip):**
## Onsdag ## Onsdag
**KC(Alexander):** Afholdte udviklermøde **KC(Alexander):**
**SM(Reimar):** Tilføjet billeder til reviews, lavet backend til at vise bruger-information på review-liste **SM(Reimar):**
**PO(Philip):** Kiggede på openai. planlage møde med kundegruppe **PO(Philip):**
## Torsdag ## Torsdag
**KC(Alexander):** **KC(Alexander):**
**SM(Reimar):** Holdt møde, fikset min cykel **SM(Reimar):**
**PO(Philip):** fik overblik over hvad vi hver især skulle vise til kundegruppe mødet. openai blev ikke færdig **PO(Philip):**
## Fredag ## Fredag
**KC(Alexander):** **KC(Alexander):**
**SM(Reimar):** Lavet frontend til at vise bruger-info på review-liste, implementeret sletning af reviews, fikset nogle fejl ved image upload **SM(Reimar):**
**PO(Philip):** Møde med kundegruppen hvor vi viste alt funktionallitet, dog ikke at det virkede fuldendt **PO(Philip):**
# Uge 38 ## Fredag
## Mandag **KC(Alexander):**
**SM(Alexander):** **SM(Reimar):**
**PO(Reimar):** **PO(Philip):**
**KC(Philip):** openai blev færdiglavet med guidebook
## Tirsdag
**SM(Alexander):**
**PO(Reimar):**
**KC(Philip):** for overblik over hvad vi skal sige til eksamen

View File

@ -1,3 +1,3 @@
# Case beskrivelse # Case beskrivelse
PDF[Case](https://edumercantec.sharepoint.com/:b:/s/24Q3H4-AppprogrammeringServerogmetodik/EbZ4bfQ_4xhJlUp6dVJMmbEBq7saaqfJJ8RuHLuPnVe6hw?e=yQoxXO) Som kunde skal I beskrive det product som I ønsker! For eksempler se her - [Notion](https://mercantec.notion.site/Casebeskrivelse-og-Kravspec-60eb806216074896ae1b3c7f14d9b2b6?pvs=4)

View File

@ -1,22 +0,0 @@
## Setting up
### C# backend
In the `API` folder, copy `appsettings.example.json` to `appsettings.json` and fill out the values. `AccessKey` and `SecretKey` are for the Cloudflare R2 service.
Run `dotnet ef database update` and then `dotnet run`.
### Rust backend
In the `rust-backend` folder, copy `.env.example` to `.env` and fill out the values. Make sure the JWT secret is the same on both backends.
Rust can be installed from <https://rustup.rs>. After installation, run `rustup default stable` in the terminal.
To start the backend, run `cargo run`.
### Flutter
In the `Mobile` folder, copy `environment.example.json` to `environment.json` and fill out the values. Also do this with `.env.example`, copying it into `.env`.
Run `flutter run --dart-define-from-file environment.json`.

View File

@ -3,7 +3,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application <application
android:label="SkanTravels" android:label="mobile"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,4 +0,0 @@
#!/bin/sh
java -version
flutter build apk --split-per-abi --no-shrink --dart-define-from-file environment.prod.json

View File

@ -1,4 +0,0 @@
{
"AUTH_SERVICE_HOST": "https://skantravels.reim.ar",
"APP_SERVICE_HOST": "https://skantravels.reim.ar"
}

View File

@ -20,11 +20,7 @@ import 'services/api.dart' as api;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: ".env"); await dotenv.load(fileName: ".env");
// Refresh JWT on startup
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
if (prefs.getString("token") != null && prefs.getString("refresh-token") != null) { if (prefs.getString("token") != null && prefs.getString("refresh-token") != null) {
final token = await api.request(null, api.ApiService.auth, "POST", "/RefreshToken", {'refreshToken': prefs.getString("refresh-token")}); final token = await api.request(null, api.ApiService.auth, "POST", "/RefreshToken", {'refreshToken': prefs.getString("refresh-token")});
@ -272,29 +268,20 @@ class _MyHomePageState extends State<MyHomePage> {
final List<dynamic> boundingBox = location[0]['boundingbox']; final List<dynamic> boundingBox = location[0]['boundingbox'];
_getOpenStreetMapData(LatLng(double.parse(boundingBox[0]), double.parse(boundingBox[2])), LatLng(double.parse(boundingBox[1]), double.parse(boundingBox[3]))); _getOpenStreetMapData(LatLng(double.parse(boundingBox[0]), double.parse(boundingBox[2])), LatLng(double.parse(boundingBox[1]), double.parse(boundingBox[3])));
} }
Future<void> _getCurrentLocation() async { Future<void> _getCurrentLocation() async {
LocationPermission? permission; LocationPermission permission = await Geolocator.checkPermission();
try { if(permission != LocationPermission.always || permission != LocationPermission.whileInUse){
permission = await Geolocator.requestPermission();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error retrieving location: $e')));
return;
} }
else{
if (permission != LocationPermission.always && permission != LocationPermission.whileInUse) { await Geolocator.requestPermission();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Location permission denied')));
return;
} }
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high); Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
_mapController.move(LatLng(position.latitude, position.longitude), 10); _mapController.move(LatLng(position.latitude, position.longitude), 10);
setState(() { setState(() {
_userPosition = LatLng(position.latitude, position.longitude); _userPosition = LatLng(position.latitude, position.longitude);
}); });
LatLngBounds bounds = _mapController.camera.visibleBounds; LatLngBounds bounds = _mapController.camera.visibleBounds;
_getOpenStreetMapData(LatLng(bounds.southWest.latitude, bounds.southWest.longitude),LatLng(bounds.northEast.latitude, bounds.northEast.longitude)); _getOpenStreetMapData(LatLng(bounds.southWest.latitude, bounds.southWest.longitude),LatLng(bounds.northEast.latitude, bounds.northEast.longitude));

View File

@ -1,9 +1,6 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mobile/base/sidemenu.dart'; import 'package:mobile/base/sidemenu.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'models.dart' as models; import 'models.dart' as models;
import 'services/api.dart' as api; import 'services/api.dart' as api;
@ -15,86 +12,22 @@ class ReviewListPage extends StatefulWidget {
} }
class _ReviewListState extends State<ReviewListPage> { class _ReviewListState extends State<ReviewListPage> {
List<models.User> _users = [];
List<models.Review> _reviews = [];
String? _currentUserId;
models.User? _getReviewUser(models.Review review) {
try {
return _users.firstWhere((user) => user.id == review.userId);
} catch(e) {
return null;
}
}
void _confirmDeleteReview(models.Review review) {
showDialog(context: context, builder: (BuildContext context) =>
AlertDialog(
title: const Text('Delete review'),
content: const Text('Are you sure you want to delete this review?'),
actions: [
TextButton(child: const Text('Cancel'), onPressed: () => Navigator.pop(context)),
TextButton(child: const Text('Delete', style: TextStyle(color: Colors.red)), onPressed: () => _deleteReview(review)),
]
)
);
}
void _deleteReview(models.Review review) async {
Navigator.pop(context);
final response = await api.request(context, api.ApiService.app, 'DELETE', '/reviews/${review.id}', null);
if (response == null) return;
setState(() {
_reviews = _reviews.where((r) => r.id != review.id).toList();
});
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Review deleted successfully')));
}
@override
void didChangeDependencies() async {
super.didChangeDependencies();
final prefs = await SharedPreferences.getInstance();
_currentUserId = prefs.getString('id');
final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList;
_reviews = arg.reviews;
if (_reviews.isEmpty || !mounted) {
return;
}
final userIds = _reviews.map((review) => review.userId).toSet().toList();
final response = await api.request(context, api.ApiService.auth, 'GET', '/api/Users/UsersByIds?userIds=' + userIds.join(','), null);
if (response == null) return;
setState(() {
_users = (jsonDecode(response) as List<dynamic>).map((user) => models.User.fromJson(user)).toList();
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList; final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList;
final reviews = arg.reviews;
final place = arg.place; final place = arg.place;
return SideMenu( return SideMenu(
selectedIndex: -1, selectedIndex: -1,
body: Scaffold( body: Scaffold(
backgroundColor: const Color(0xFFF9F9F9), backgroundColor: const Color(0xFFF9F9F9),
body: _reviews.isEmpty body: SingleChildScrollView(child: Container(
? const Center(child: Text('No reviews yet. Be the first to review this place'))
: SingleChildScrollView(child: Container(
decoration: const BoxDecoration(color: Color(0xFFF9F9F9)), decoration: const BoxDecoration(color: Color(0xFFF9F9F9)),
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: Column(children: [ child: Column(children:
for (final review in _reviews) reviews.map((review) => Container(
Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
@ -111,50 +44,31 @@ class _ReviewListState extends State<ReviewListPage> {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( const Padding(
padding: const EdgeInsets.only(top: 3), padding: EdgeInsets.only(top: 3),
child: child: Icon(Icons.rate_review, color: Colors.purple, size: 36),
_getReviewUser(review)?.profilePicture.isNotEmpty == true
? ClipOval(
child: Image(
image: NetworkImage(_getReviewUser(review)!.profilePicture),
height: 36,
width: 36,
fit: BoxFit.cover,
),
)
: const Icon(
Icons.account_circle,
size: 36,
color: Colors.grey,
)
), ),
const SizedBox(width: 20), const SizedBox(width: 20),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row(children: [ Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24)),
Expanded(child: Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24))),
if (review.userId == _currentUserId) IconButton(onPressed: () => _confirmDeleteReview(review), icon: const Icon(Icons.delete, color: Colors.grey)),
]),
Text(review.content), Text(review.content),
const SizedBox(height: 10), const SizedBox(height: 10),
if (review.image != null) Image.network(review.image!.imageUrl, height: 200), if (review.image != null) Image.network(review.image!.imageUrl, height: 200,),
if (review.image != null) const SizedBox(height: 15), if (review.image != null) const SizedBox(height: 15),
Row(children: [ Row(children: [
for (var i = 0; i < review.rating; i++) const Icon(Icons.star, color: Colors.yellow), for (var i = 0; i < review.rating; i++) const Icon(Icons.star, color: Colors.yellow),
for (var i = review.rating; i < 5; i++) const Icon(Icons.star_border), for (var i = review.rating; i < 5; i++) const Icon(Icons.star_border),
]), ]),
const SizedBox(height: 10),
Text('Submitted by ' + (_getReviewUser(review)?.username ?? ''), style: const TextStyle(color: Colors.grey, fontSize: 12)),
], ],
), ),
), ),
], ],
), ),
)).toList(),
), ),
]),
)), )),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () async { onPressed: () async {
@ -164,7 +78,7 @@ class _ReviewListState extends State<ReviewListPage> {
} }
final review = await Navigator.pushNamed(context, '/create-review', arguments: place) as models.Review?; final review = await Navigator.pushNamed(context, '/create-review', arguments: place) as models.Review?;
if (review != null) _reviews.add(review); if (review != null) reviews.add(review);
}, },
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
focusColor: Colors.blueGrey, focusColor: Colors.blueGrey,

View File

@ -13,8 +13,6 @@ enum ApiService {
} }
Future<String?> request(BuildContext? context, ApiService service, String method, String path, dynamic body) async { Future<String?> request(BuildContext? context, ApiService service, String method, String path, dynamic body) async {
var debug = '$method $path\n $body\n';
final messenger = context != null ? ScaffoldMessenger.of(context) : null; final messenger = context != null ? ScaffoldMessenger.of(context) : null;
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
@ -49,22 +47,18 @@ Future<String?> request(BuildContext? context, ApiService service, String method
); );
} }
} catch (e) { } catch (e) {
debugPrint(e.toString());
messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server'))); messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server')));
debug += 'FAILED\n $e';
debugPrint(debug);
return null; return null;
} }
debug += 'HTTP ${response.statusCode}\n ${response.body}';
debugPrint(debug);
if (response.statusCode < 200 || response.statusCode >= 300) { if (response.statusCode < 200 || response.statusCode >= 300) {
try { try {
final json = jsonDecode(response.body); final json = jsonDecode(response.body);
messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title']))); messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title'])));
debugPrint('API error: ' + json['message']);
} catch (e) { } catch (e) {
debugPrint(e.toString());
messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})'))); messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})')));
} }
return null; return null;

View File

@ -210,10 +210,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "3.0.2"
flutter_map: flutter_map:
dependency: "direct main" dependency: "direct main"
description: description:
@ -428,10 +428,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "3.0.0"
lists: lists:
dependency: transitive dependency: transitive
description: description:

View File

@ -55,7 +55,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^4.0.0 flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec

View File

@ -6,8 +6,3 @@ Vi vil udvikle en TuristApp for SkanTravles, som gør brug af telefonens sensore
[Kravspecifikation](Docs/kravspec.md) [Kravspecifikation](Docs/kravspec.md)
[Logbog](Docs/SCRUM%20Logbog.md) [Logbog](Docs/SCRUM%20Logbog.md)
[Setup guide](Docs/setup.md)
[Figma design](https://www.figma.com/design/r5EkReHw6NVC3gd9DeIc0P/Gruppe-6---SkanTravels?node-id=0-1&t=FxBQswcVrzRnhR2T-1)

View File

@ -1,8 +0,0 @@
JWT_SECRET=DenHerMåAldrigVæreOffentligKunIDetteDemoProjekt
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_ENDPOINT_URL=
AWS_REGION=
R2_BUCKET_NAME=
R2_BUCKET_URL=

View File

@ -6,7 +6,6 @@ After=network.target
Environment=RUST_BACKEND_PORT=5002 Environment=RUST_BACKEND_PORT=5002
Environment=RUST_BACKEND_DB=/home/reimar/skantravels/database-rust.sqlite3 Environment=RUST_BACKEND_DB=/home/reimar/skantravels/database-rust.sqlite3
ExecStart=/home/reimar/skantravels/skantravels ExecStart=/home/reimar/skantravels/skantravels
WorkingDirectory=/home/reimar/skantravels
Type=simple Type=simple
[Install] [Install]

View File

@ -219,9 +219,7 @@ async fn create_review(auth: AuthorizedUser, data: web::Data<AppData>, input: we
content: input.content.clone(), content: input.content.clone(),
rating: input.rating.clone(), rating: input.rating.clone(),
image_id: input.image_id, image_id: input.image_id,
image: input.image_id.and_then(|image_id| { image: None,
db.query_row("SELECT * FROM images WHERE id = :id LIMIT 1", &[(":id", &image_id.to_string())], |row| Image::from_row(row)).ok()
}),
}), }),
Err(_) => HttpResponse::InternalServerError().finish(), Err(_) => HttpResponse::InternalServerError().finish(),
} }
@ -330,7 +328,7 @@ async fn main() -> std::io::Result<()> {
.app_data(web::Data::new(AppData { .app_data(web::Data::new(AppData {
database: conn.clone(), database: conn.clone(),
})) }))
.app_data(web::PayloadConfig::new(30 * 1024 * 1024)) .app_data(web::PayloadConfig::new(8_388_608))
.service(healthcheck) .service(healthcheck)
.service(authorized) .service(authorized)
.service(favorites) .service(favorites)