Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions PayForMe/Model/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ class Project: Codable, Identifiable {
var bills: [Bill]
var me: Int?

convenience init(name: String, password: String, token: String, backend: ProjectBackend, url: URL) {
self.init(name: name, password: password, token: token, backend: backend, url: url, id: nil)
let projectId: String

convenience init(name: String, password: String, token: String, backend: ProjectBackend, url: URL, projectId: String) {
self.init(name: name, password: password, token: token, backend: backend, url: url, id: nil, projectId: projectId)
}

fileprivate init(name: String, password: String, token: String, backend: ProjectBackend, url: URL, id: Int?, me: Int? = nil) {
fileprivate init(name: String, password: String, token: String, backend: ProjectBackend, url: URL, id: Int?, me: Int? = nil, projectId: String) {
self.name = name
self.password = password
self.token = token
Expand All @@ -33,6 +35,7 @@ class Project: Codable, Identifiable {
members = [:]
bills = []
self.me = me
self.projectId = projectId
}
}

Expand All @@ -49,15 +52,17 @@ struct StoredProject: Codable {
let backend: ProjectBackend
var id: Int?
let me: Int?
let projectId: String

init(name: String, password: String, token: String, url: URL, backend: ProjectBackend) {
init(name: String, password: String, token: String, url: URL, backend: ProjectBackend, projectId: String) {
self.name = name
self.password = password
self.token = token
self.url = url
self.backend = backend
id = nil
me = nil
self.projectId = projectId
}

init(project: Project) {
Expand All @@ -68,10 +73,11 @@ struct StoredProject: Codable {
backend = project.backend
id = project.id
me = project.me
projectId = project.projectId
}

func toProject() -> Project {
Project(name: name, password: password, token: token, backend: backend, url: url, id: id!, me: me)
Project(name: name, password: password, token: token, backend: backend, url: url, id: id!, me: me, projectId: projectId)
}
}

Expand Down Expand Up @@ -101,10 +107,10 @@ enum ProjectBackend: Int, Codable {
}
}

let previewProject = Project(name: "TestProject", password: "TestPassword", token: "asdasdas", backend: .cospend, url: URL(string: "https://testserver.de")!, id: 0)
let previewProject = Project(name: "TestProject", password: "TestPassword", token: "asdasdas", backend: .cospend, url: URL(string: "https://testserver.de")!, id: 0, projectId: "TestProject")
let previewProjects = [
previewProject,
Project(name: "test1", password: "test23", token: "dasdasa", backend: .cospend, url: URL(string: "https://testserver.de")!, id: 1),
Project(name: "test2", password: "test45", token: "123123122", backend: .cospend, url: URL(string: "https://testserver.de")!, id: 2),
Project(name: "test1", password: "test23", token: "dasdasa", backend: .cospend, url: URL(string: "https://testserver.de")!, id: 1, projectId: "test1"),
Project(name: "test2", password: "test45", token: "123123122", backend: .cospend, url: URL(string: "https://testserver.de")!, id: 2, projectId: "test2"),
]
let demoProject = Project(name: "study-group", password: "no-pass", token: "9da50e410157dc1ca63e594af022f3a2", backend: .cospend, url: URL(string: "https://intranet.mayflower.de")!, id: 1)
let demoProject = Project(name: "study-group", password: "no-pass", token: "9da50e410157dc1ca63e594af022f3a2", backend: .cospend, url: URL(string: "https://intranet.mayflower.de")!, id: 1, projectId: "study-group")
60 changes: 55 additions & 5 deletions PayForMe/Services/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,43 @@ class NetworkService {
}
let apiProject = try JSONDecoder().decode(APIProject.self, from: data)

return Project(name: apiProject.name, password: project.password, token: project.token, backend: project.backend, url: project.url)
return Project(name: apiProject.name, password: project.password, token: project.token, backend: project.backend, url: project.url, projectId: apiProject.id)
}

func getProjectName(invite: InviteData) async throws -> Project {
var request = URLRequest(url: URL(string: invite.url + "/api/projects/" + invite.project)!)
request.httpMethod = "GET"
request.setValue(
"Bearer \(invite.token)",
forHTTPHeaderField: "Authorization"
)

let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode / 100 == 2 else {
throw HTTPError.statuscode
}
let apiProject = try JSONDecoder().decode(APIProject.self, from: data)

return Project(name: apiProject.name, password: "", token: invite.token, backend: ProjectBackend.iHateMoney, url: URL(string: invite.url)!, projectId: apiProject.id)
}

func fetchInvite(_ invite: InviteData?) async throws {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methode erzeugt nur einen Log, sinnvoll im Produktivcode?

guard let invite = invite, let url = URL(string: invite.url) else {
return
}

var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue(
"Bearer \(invite.token)",
forHTTPHeaderField: "Authorization"
)

let (data, response) = try await URLSession.shared.data(for: request)
if let httpResponse = response as? HTTPURLResponse {
print("status:", httpResponse.statusCode)
}
print(String(data: data, encoding: .utf8) ?? "")
}

func postBillPublisher(bill: Bill) -> AnyPublisher<Bool, Never> {
Expand Down Expand Up @@ -147,13 +183,21 @@ class NetworkService {
.replaceError(with: false)
.eraseToAnyPublisher()
}


private func baseURLFor(_ project: Project, suffix: String) -> URL {
var url = project.url
.appendingPathComponent(project.backend.staticPath)
.appendingPathComponent(project.token)

if project.backend == .cospend {
url = url.appendingPathComponent(project.password)
url = url
.appendingPathComponent(project.projectId)
.appendingPathComponent(project.password)
}

if project.backend == .iHateMoney {
url = url
.appendingPathComponent(project.projectId)
}
if suffix.isEmpty {
return url
Expand Down Expand Up @@ -181,8 +225,14 @@ class NetworkService {
request = URLRequest(url: requestURL)

if project.backend == .iHateMoney {
guard let authString = "\(project.token):\(project.password)".data(using: .utf8)?.base64EncodedString() else { fatalError("error generating authString. THIS SHOULD NOT HAPPEN") }
request.setValue("Basic \(authString)", forHTTPHeaderField: "Authorization")
if project.password.isEmpty {
request.setValue("Bearer \(project.token)", forHTTPHeaderField: "Authorization")
} else {
guard let authString = "\(project.token):\(project.password)".data(using: .utf8)?.base64EncodedString() else {
fatalError("error generating authString. THIS SHOULD NOT HAPPEN")
}
request.setValue("Basic \(authString)", forHTTPHeaderField: "Authorization")
}

if !params.isEmpty {
request.httpBody = try? JSONSerialization.data(withJSONObject: params)
Expand Down
6 changes: 1 addition & 5 deletions PayForMe/Services/ProjectManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ class ProjectManager: ObservableObject {

func openedByURL(url: URL) {
let data = url.decodeCospendString()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gehört hier dann noch der Fall für IHM hin? decodeIHateMoneyString()

guard let _ = data.server,
let _ = data.project
else {
return
}
guard data != nil else { return }
openedByURL = url
}

Expand Down
8 changes: 7 additions & 1 deletion PayForMe/Services/StorageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ class StorageService {
table.add(column: "me")
})
}
migrator.registerMigration("v4") { db in
try db.alter(table: "storedProject", body: { table in
table.add(column: "projectId")
})
try db.execute(sql: "UPDATE storedProject SET projectId = name;")
}
// #if DEBUG
//// Speed up development by nuking the database when migrations change
// migrator.eraseDatabaseOnSchemaChange = true
Expand Down Expand Up @@ -153,6 +159,6 @@ private class OldProject: Codable, Identifiable {
var bills: [Bill]

func toProject() -> StoredProject {
return StoredProject(name: name, password: password, token: name, url: url, backend: backend)
return StoredProject(name: name, password: password, token: name, url: url, backend: backend, projectId: name)
}
}
104 changes: 88 additions & 16 deletions PayForMe/Util/Util.swift

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Die drei extension URL Blöcke könnten zusammengefasst werden und die expliziten elementweisen Initialisierungen für ProjectDataWithToken / ProjectDataWithPassword dürften automatisch durch den Compiler generiert werden

Original file line number Diff line number Diff line change
Expand Up @@ -176,43 +176,115 @@ extension StringProtocol {
}
}

typealias ProjectData = (server: URL?, project: String?, passwd: String?)
protocol ProjectData {
var server: URL { get }
var project: String { get }
}

struct ProjectDataWithToken: ProjectData {
var server: URL
var project: String
var token: String

init(server: URL, project: String, token: String) {
self.server = server
self.project = project
self.token = token
}

}

struct ProjectDataWithPassword: ProjectData {
var server: URL
var project: String
var password: String?

init(server: URL, project: String, password: String?) {
self.server = server
self.project = project
self.password = password
}
}

extension URL {
func decodeMoneyBusterString() -> ProjectData {
func decodeMoneyBusterString() -> ProjectDataWithPassword? {
guard absoluteString.hasPrefix("https://net.eneiluj.moneybuster.cospend/"),
pathComponents.count >= 3, pathComponents.count <= 4 else { return (nil, nil, nil) }
return (URL(string: "https://" + pathComponents[1]), pathComponents[2], pathComponents[safe: 3])
pathComponents.count >= 3, pathComponents.count <= 4 else {
return nil
}

guard let hostUrl = URL(string: "https://" + pathComponents[1]) else {
return nil
}

let password = pathComponents[safe: 3]

return ProjectDataWithPassword(
server: hostUrl,
project: pathComponents[2],
password: password
)
}
}

extension URL {
func decodeCospendString() -> ProjectData {
func decodeCospendString() -> ProjectDataWithPassword? {
guard let host = host,
let scheme = scheme,
scheme.localizedCaseInsensitiveContains("cospend")
else {
return (nil, nil, nil)
return nil
}
var hostString = "https://\(host)"

var hostString = host

if let port = port {hostString += ":\(port)"}

if pathComponents.count > 3 {
hostString += "/" + pathComponents[1..<(pathComponents.count - 2)].joined(separator: "/")
}

return (URL(string: hostString),
pathComponents[safe: pathComponents.count - 2],
pathComponents.last)

guard
let hostUrl = URL(string: "https://" + hostString),
let project = pathComponents[safe: pathComponents.count - 2],
let password = pathComponents.last
else { return nil }

return ProjectDataWithPassword(
server: hostUrl,
project: project,
password: password)
}
}

extension URL {
func decodeIHateMoenyString() -> ProjectDataWithToken? {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tippfehler bei Money lieber frühzeitig korrigieren

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Und, falls Funktion erhalten bleiben soll, host mit let anstatt var? :)

guard var host = host, let scheme = scheme, scheme.localizedCaseInsensitiveContains("ihatemoney") else {
return nil
}

let hostUrl = "https://" + host

guard
let url = URL(string: hostUrl),
let project = pathComponents[safe: pathComponents.count - 3],
let token = pathComponents.last
else { return nil}

return ProjectDataWithToken(server: url, project: project, token: token)
}
}

extension URL {
func decodeQRCode() -> ProjectData {
guard let scheme = scheme else { return (nil, nil, nil) }
return scheme.contains("cospend") ? decodeCospendString() : decodeMoneyBusterString()
func decodeQRCode() -> ProjectData? {
guard let scheme = scheme else { return nil }
if scheme.contains("cospend") {
return decodeCospendString()
} else if scheme.contains("ihatemoney") {
return decodeIHateMoenyString()
} else {
return decodeMoneyBusterString()
}
}
}

Expand Down
Loading
Loading