Compare commits

..

No commits in common. "main" and "v1.1.1" have entirely different histories.
main ... v1.1.1

7 changed files with 85 additions and 280 deletions

View File

@ -1,14 +0,0 @@
# Files and directories created by pub.
Dockerfile
build/
.dart_tool/
.git/
.github/
.gitignore
.packages
.data/
.tmp/
# Conventional directory for build output.
build/

View File

@ -1,8 +1,8 @@
# Specify the Dart SDK base image version using dart:<version> (ex: dart:2.12)
FROM dart:stable AS compile
FROM dart:stable AS build
# Resolve app dependencies.
WORKDIR /lchatserver
WORKDIR /app
COPY pubspec.* ./
RUN dart pub get
@ -10,18 +10,14 @@ RUN dart pub get
COPY . .
# Ensure packages are still up-to-date if anything has changed
RUN dart pub get --offline
RUN dart compile exe bin/tcp_server.dart -o bin/tcp_server
RUN dart compile exe bin/server.dart -o bin/server
FROM ubuntu:latest
RUN apt-get update && apt-get -y install libsqlite3-0 libsqlite3-dev
# Copy the previously built executable into the scratch layer
RUN mkdir /lchatserver
COPY --from=compile /runtime/ /lchatserver/
COPY --from=compile /lchatserver/bin/tcp_server /lchatserver/bin/
# Build minimal serving image from AOT-compiled `/server` and required system
# libraries and configuration files stored in `/runtime/` from the build stage.
FROM scratch
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server /app/bin/
# Start server.
EXPOSE 20706
WORKDIR /lchatserver/bin
CMD ["/lchatserver/bin/tcp_server"]
CMD ["/app/bin/server"]

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-06 15:44:16
* @LastEditTime : 2022-10-23 10:33:58
* @LastEditTime : 2022-10-17 22:56:11
* @Description :
*/
@ -15,21 +15,20 @@ import 'package:tcp_server/tcpcontroller/request.dart';
import 'package:tcp_server/tcpcontroller/response.dart';
void main(List<String> arguments) async {
//Set port
var address = arguments.isEmpty ? '127.0.0.1' : arguments[0];
//Set address
var port = arguments.isEmpty ? 20706 : int.tryParse(arguments[0]) ?? 20706;
print('[L] [STARTUP ]-----------------------');
print('[L] Running at directory ${Directory.current.path}');
var port = arguments.length < 2 ? 20706 : int.tryParse(arguments[1]) ?? 20706;
//Create nessesary working directories
await Directory('${Directory.current.path}/.tmp').create();
await Directory('${Directory.current.path}/.data').create();
await Directory('${Directory.current.path}/.data/.tmp').create();
await Directory('${Directory.current.path}/.data/files').create();
await DataBaseHelper().initialize();
Map<int, List<TCPController>> tokenMap = {};
Map<TCPController, Future<int>> controllerMap = {};
var listenSocket = await ServerSocket.bind(InternetAddress.anyIPv4, port);
var listenSocket = await ServerSocket.bind(address, port);
listenSocket.listen(
(socket) {
var controller = TCPController(socket: socket);
@ -38,31 +37,17 @@ void main(List<String> arguments) async {
onError: (_) {
print('[L] [EXCEPTION]-----------------------');
print('[L] TCP Controller ran into exception');
print('[L] socket: ${controller.socket.address}:${controller.socket.port}');
print('[L] Remote: ${controller.socket.remoteAddress}:${controller.socket.remotePort}');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
},
onDone: () {
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
},
cancelOnError: true
}
);
controller.requestStreamBroadcast.listen(
(request) async {
print('[L] [INCOMING ]-----------------------');
print('[L] Incoming from ${controller.socket.remoteAddress}:${controller.socket.remotePort}');
if(request.requestType == RequestType.sendMessage) {
print('[L] Message: (Message body)');
}
else if(request.requestType == RequestType.modifyProfile) {
print('[L] Profile: (Profile body)');
}
else {
print('[L] Message: ${request.toJSON}');
}
print('[L] Message: ${request.toJSON}');
if(!(await DataBaseHelper().isTokenValid(tokenid: request.tokenID))) {
if(controllerMap[controller] == null) {
controllerMap[controller] = (() async => (await DataBaseHelper().createToken()))();
@ -84,38 +69,17 @@ void main(List<String> arguments) async {
switch(request.requestType) {
case RequestType.checkState: {
var response = await onCheckState(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.register: {
var response = await onRegister(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.login: {
var response = await onLogin(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.logout: {
@ -125,38 +89,17 @@ void main(List<String> arguments) async {
}
case RequestType.profile: {
var response = await onFetchProfile(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.modifyProfile: {
var response = await onModifyProfile(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.modifyPassword: {
var response = await onModifyPassword(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.sendMessage: {
@ -180,22 +123,16 @@ void main(List<String> arguments) async {
);
for(var controller in targetControllers) {
try {
print('[L] [MSGFOWARD]-----------------------');
print('[L] Forwarding message to ${controller.socket.remoteAddress}:${controller.socket.remotePort}');
controller.outStream.add(forwardResponse);
} catch(e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
continue;
print(e);
}
}
// //Update Fetch Histories
// await DataBaseHelper().setFetchHistoryFor(
// tokenID: device,
// newTimeStamp: message.timestamp
// );
//Update Fetch Histories
await DataBaseHelper().setFetchHistoryFor(
tokenID: device,
newTimeStamp: message.timestamp
);
}
var targetUserID = message.receiverID;
var targetDevices = await DataBaseHelper().fetchTokenIDsViaUserID(userID: targetUserID);
@ -218,121 +155,51 @@ void main(List<String> arguments) async {
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
continue;
}
}
// //Update Fetch Histories
// await DataBaseHelper().setFetchHistoryFor(
// tokenID: device,
// newTimeStamp: message.timestamp
// );
//Update Fetch Histories
await DataBaseHelper().setFetchHistoryFor(
tokenID: device,
newTimeStamp: message.timestamp
);
}
var response = await onSendMessage(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.fetchMessage: {
var response = await onFetchMessage(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.findFile: {
var response = await onFindFile(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.fetchFile: {
var response = await onFetchFile(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.searchUser: {
var response = await onSearchUser(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
case RequestType.addContact: {
var response = await onAddContact(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
var contactResponse = await onFetchContact(
TCPRequest.fromData(
type: RequestType.fetchContact,
body: {},
tokenID: request.tokenID
),
socket
);
controller.outStream.add(contactResponse);
controller.outStream.add(response);
break;
}
case RequestType.fetchContact: {
var response = await onFetchContact(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
break;
}
case RequestType.ackFetch: {
onAckFetch(request, socket);
controller.outStream.add(response);
break;
}
case RequestType.unknown: {
var response = await onUnknownRequest(request, socket);
try {
controller.outStream.add(response);
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
var token = controllerMap[controller];
controllerMap.remove(controller);
tokenMap[token]?.remove(controller);
}
controller.outStream.add(response);
break;
}
default: {

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-06 16:15:01
* @LastEditTime : 2022-10-22 21:08:27
* @LastEditTime : 2022-10-18 14:12:22
* @Description :
*/
@ -25,7 +25,7 @@ class DataBaseHelper {
Future<void> initialize() async {
_database = await databaseFactoryFfi.openDatabase(
'${Directory.current.path}/.data/.tmp/database.db',
'${Directory.current.path}/.tmp/database.db',
options: OpenDatabaseOptions(
version: 1,
onCreate: (db, version) async {
@ -34,7 +34,7 @@ class DataBaseHelper {
'''
CREATE TABLE users (
userid integer primary key autoincrement,
username text unique not null,
username text not null,
passwd text not null,
avatar text
);
@ -241,29 +241,17 @@ class DataBaseHelper {
//Insert into users
try {
await _database.transaction((txn) async {
var result = await txn.query(
'users',
where: 'username = ?',
whereArgs: [
identity.userName
]
);
if(result.isNotEmpty) {
throw Exception('Username already exists');
}
await txn.insert(
'users',
{
'username': identity.userName,
'passwd': identity.userPasswd,
'avatar': null
},
conflictAlgorithm: ConflictAlgorithm.rollback
);
});
} catch (e) {
rethrow;
await _database.insert(
'users',
{
'username': identity.userName,
'passwd': identity.userPasswd,
'avatar': null
},
conflictAlgorithm: ConflictAlgorithm.rollback
);
} catch (conflict) {
throw Exception(['Database failure', conflict.toString()]);
}
//Get new userid
@ -370,7 +358,7 @@ class DataBaseHelper {
//Fetch unfetched messages
var unfetchMsgQueryResult = await _database.query(
'msgs left outer join msgfiles on msgs.md5encoded = msgfiles.msgmd5',
'msgs join msgfiles on msgs.md5encoded = msgfiles.msgmd5',
columns: [
'msgs.userid as userid',
'msgs.targetid as targetid',
@ -403,19 +391,19 @@ class DataBaseHelper {
}).toList();
//Set new fetch history
// if(unfetchMsgQueryResult.isNotEmpty) {
// await _database.update(
// 'histories',
// {
// 'lastfetch': unfetchMsgQueryResult[0]['timestamp']
// },
// where: 'tokenid = ? and userid = ?',
// whereArgs: [
// tokenID,
// userID
// ]
// );
// }
if(unfetchMsgQueryResult.isNotEmpty) {
await _database.update(
'histories',
{
'lastfetch': unfetchMsgQueryResult[0]['timestamp']
},
where: 'tokenid = ? and userid = ?',
whereArgs: [
tokenID,
userID
]
);
}
//return result
return unfetchMessages;
@ -629,10 +617,6 @@ class DataBaseHelper {
'username': userInfo.userName,
'avatar': userInfo.userAvatar
},
where: 'userid = ?',
whereArgs: [
currentUserID
],
conflictAlgorithm: ConflictAlgorithm.rollback
);
} catch (conflict) {

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-08 20:52:48
* @LastEditTime : 2022-10-22 20:56:15
* @LastEditTime : 2022-10-15 00:40:24
* @Description :
*/
@ -276,14 +276,6 @@ Future<TCPResponse> onFetchContact(TCPRequest request, Socket socket) async {
}
}
void onAckFetch(TCPRequest request, Socket socket) async {
//Update Fetch Histories
await DataBaseHelper().setFetchHistoryFor(
tokenID: request.tokenID,
newTimeStamp: request.body['timestamp'] as int,
);
}
Future<TCPResponse> onUnknownRequest(TCPRequest request, Socket socket) async {
return TCPResponse(
type: ResponseType.fromRequestType(request.requestType),

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-08 15:10:04
* @LastEditTime : 2022-10-22 21:20:57
* @LastEditTime : 2022-10-19 10:41:12
* @Description :
*/
@ -58,21 +58,18 @@ class TCPController {
print('[L] Remote: ${socket.remoteAddress}:${socket.remotePort}');
print('[L] Local: ${socket.address}:${socket.port}');
Future(() async {
try {
await for(var request in socket) {
_pullRequest(request);
await Future.delayed(const Duration(microseconds: 0));
}
} catch (e) {
_requestStreamController.addError(e);
_responseStreamController.addError(e);
await for(var request in socket) {
_pullRequest(request);
await Future.delayed(const Duration(microseconds: 0));
}
}).then((_) {
print('[L] [CLOSED ]-----------------------');
print('[L] Connection closed: ${socket.address}:${socket.port}');
print('[L] Connection closed: ${socket.address}:${socket.port}<-${socket.remoteAddress}:${socket.remotePort}');
_requestStreamController.close();
_responseStreamController.close();
});
}).onError((error, stackTrace) {
print(error);
_requestStreamController.addError(error ?? Error());
},);
// socket.listen(
// _pullRequest,
// onError: (e) {
@ -93,8 +90,7 @@ class TCPController {
await socket.addStream(response.stream);
}
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
print('[E] Adding bytes to socket stream failed');
print(e);
await socket.flush();
socket.close();
}
@ -136,7 +132,7 @@ class TCPController {
//Create a future that listens to the status of the payload transmission
() {
var payloadPullStream = _payloadPullStreamController.stream;
var tempFile = File('${Directory.current.path}/.data/.tmp/${DateTime.now().microsecondsSinceEpoch}$_fileCounter')..createSync();
var tempFile = File('${Directory.current.path}/.tmp/${DateTime.now().microsecondsSinceEpoch}$_fileCounter')..createSync();
_fileCounter += 1;
_fileCounter %= 1000;
Future(() async {
@ -205,11 +201,6 @@ class TCPController {
required List<int> requestBytes,
required File tempFile
}) async {
try{
_requestStreamController.add(TCPRequest(requestBytes, tempFile));
} catch (e) {
print('[E] [EXCEPTION]-----------------------');
print('[E] Adding bytes to request stream failed');
}
_requestStreamController.add(TCPRequest(requestBytes, tempFile));
}
}

View File

@ -1,7 +1,7 @@
/*
* @Author : Linloir
* @Date : 2022-10-08 15:14:26
* @LastEditTime : 2022-10-22 20:54:40
* @LastEditTime : 2022-10-09 22:56:26
* @Description :
*/
import 'dart:convert';
@ -16,7 +16,6 @@ enum RequestType {
modifyPassword('MODIFYPASSWD'), //Modify user password
modifyProfile ('MODIFYPROFILE'), //Modify user profile
sendMessage ('SENDMSG'), //Send message
ackFetch ('ACKFETCH'), //Ack fetched messages, update fetch history
fetchMessage ('FETCHMSG'), //Fetch message
findFile ('FINDFILE'), //Find file by md5 before transmitting the file
fetchFile ('FETCHFILE'), //Fetch file and file md5 by message md5
@ -41,16 +40,6 @@ class TCPRequest {
File? payload;
TCPRequest(List<int> data, this.payload): _data = jsonDecode(String.fromCharCodes(data));
TCPRequest.fromData({
required RequestType type,
required Map<String, Object?> body,
required int? tokenID,
this.payload
}): _data = {
'request': type.value,
'tokenid': tokenID,
'body': body
};
TCPRequest.none(): _data = {};
String get toJSON => jsonEncode(_data);