mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-19 21:00:40 +08:00
211 lines
7.0 KiB
Swift
211 lines
7.0 KiB
Swift
import Foundation
|
|
|
|
/// A team/organization that users can belong to
|
|
public actor Team {
|
|
private let client: APIClient
|
|
|
|
public nonisolated let id: String
|
|
public private(set) var displayName: String
|
|
public private(set) var profileImageUrl: String?
|
|
public private(set) var clientMetadata: [String: Any]
|
|
public private(set) var clientReadOnlyMetadata: [String: Any]
|
|
|
|
init(client: APIClient, json: [String: Any]) {
|
|
self.client = client
|
|
self.id = json["id"] as? String ?? ""
|
|
self.displayName = json["display_name"] as? String ?? ""
|
|
self.profileImageUrl = json["profile_image_url"] as? String
|
|
self.clientMetadata = json["client_metadata"] as? [String: Any] ?? [:]
|
|
self.clientReadOnlyMetadata = json["client_read_only_metadata"] as? [String: Any] ?? [:]
|
|
}
|
|
|
|
// MARK: - Update
|
|
|
|
public func update(
|
|
displayName: String? = nil,
|
|
profileImageUrl: String? = nil,
|
|
clientMetadata: [String: Any]? = nil
|
|
) async throws {
|
|
var body: [String: Any] = [:]
|
|
if let displayName = displayName { body["display_name"] = displayName }
|
|
if let profileImageUrl = profileImageUrl { body["profile_image_url"] = profileImageUrl }
|
|
if let clientMetadata = clientMetadata { body["client_metadata"] = clientMetadata }
|
|
|
|
let (data, _) = try await client.sendRequest(
|
|
path: "/teams/\(id)",
|
|
method: "PATCH",
|
|
body: body,
|
|
authenticated: true
|
|
)
|
|
|
|
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
|
self.displayName = json["display_name"] as? String ?? self.displayName
|
|
self.profileImageUrl = json["profile_image_url"] as? String
|
|
self.clientMetadata = json["client_metadata"] as? [String: Any] ?? self.clientMetadata
|
|
self.clientReadOnlyMetadata = json["client_read_only_metadata"] as? [String: Any] ?? self.clientReadOnlyMetadata
|
|
}
|
|
}
|
|
|
|
// MARK: - Delete
|
|
|
|
public func delete() async throws {
|
|
_ = try await client.sendRequest(
|
|
path: "/teams/\(id)",
|
|
method: "DELETE",
|
|
authenticated: true
|
|
)
|
|
}
|
|
|
|
// MARK: - Invite
|
|
|
|
public func inviteUser(email: String, callbackUrl: String? = nil) async throws {
|
|
var body: [String: Any] = [
|
|
"email": email,
|
|
"team_id": id
|
|
]
|
|
if let callbackUrl = callbackUrl {
|
|
body["callback_url"] = callbackUrl
|
|
}
|
|
|
|
_ = try await client.sendRequest(
|
|
path: "/team-invitations/send-code",
|
|
method: "POST",
|
|
body: body,
|
|
authenticated: true
|
|
)
|
|
}
|
|
|
|
// MARK: - List Users
|
|
|
|
public func listUsers() async throws -> [TeamUser] {
|
|
let (data, _) = try await client.sendRequest(
|
|
path: "/team-member-profiles?team_id=\(id)",
|
|
method: "GET",
|
|
authenticated: true
|
|
)
|
|
|
|
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
let items = json["items"] as? [[String: Any]] else {
|
|
return []
|
|
}
|
|
|
|
return items.map { TeamUser(from: $0) }
|
|
}
|
|
|
|
// MARK: - Invitations
|
|
|
|
public func listInvitations() async throws -> [TeamInvitation] {
|
|
let (data, _) = try await client.sendRequest(
|
|
path: "/teams/\(id)/invitations",
|
|
method: "GET",
|
|
authenticated: true
|
|
)
|
|
|
|
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
let items = json["items"] as? [[String: Any]] else {
|
|
return []
|
|
}
|
|
|
|
return items.map { TeamInvitation(client: client, teamId: id, json: $0) }
|
|
}
|
|
|
|
// MARK: - API Keys
|
|
|
|
public func listApiKeys() async throws -> [TeamApiKey] {
|
|
let (data, _) = try await client.sendRequest(
|
|
path: "/teams/\(id)/api-keys",
|
|
method: "GET",
|
|
authenticated: true
|
|
)
|
|
|
|
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
let items = json["items"] as? [[String: Any]] else {
|
|
return []
|
|
}
|
|
|
|
return items.map { TeamApiKey(from: $0) }
|
|
}
|
|
|
|
public func createApiKey(
|
|
description: String,
|
|
expiresAt: Date? = nil,
|
|
scope: String? = nil
|
|
) async throws -> TeamApiKeyFirstView {
|
|
var body: [String: Any] = ["description": description]
|
|
if let expiresAt = expiresAt {
|
|
body["expires_at"] = Int64(expiresAt.timeIntervalSince1970 * 1000)
|
|
}
|
|
if let scope = scope { body["scope"] = scope }
|
|
|
|
let (data, _) = try await client.sendRequest(
|
|
path: "/teams/\(id)/api-keys",
|
|
method: "POST",
|
|
body: body,
|
|
authenticated: true
|
|
)
|
|
|
|
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
throw StackAuthError(code: "parse_error", message: "Failed to parse API key response")
|
|
}
|
|
|
|
return TeamApiKeyFirstView(from: json)
|
|
}
|
|
}
|
|
|
|
// MARK: - Supporting Types
|
|
|
|
public struct TeamUser: Sendable {
|
|
public let id: String
|
|
public let teamProfile: TeamMemberProfile
|
|
|
|
init(from json: [String: Any]) {
|
|
// Try both "id" (from /users?team_id=) and "user_id" (from other endpoints)
|
|
self.id = json["id"] as? String ?? json["user_id"] as? String ?? ""
|
|
|
|
if let profile = json["team_profile"] as? [String: Any] {
|
|
self.teamProfile = TeamMemberProfile(
|
|
displayName: profile["display_name"] as? String,
|
|
profileImageUrl: profile["profile_image_url"] as? String
|
|
)
|
|
} else {
|
|
// If no team_profile, use display_name from user itself
|
|
self.teamProfile = TeamMemberProfile(
|
|
displayName: json["display_name"] as? String,
|
|
profileImageUrl: json["profile_image_url"] as? String
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct TeamMemberProfile: Sendable {
|
|
public let displayName: String?
|
|
public let profileImageUrl: String?
|
|
}
|
|
|
|
public actor TeamInvitation {
|
|
private let client: APIClient
|
|
private let teamId: String
|
|
|
|
public nonisolated let id: String
|
|
public let recipientEmail: String?
|
|
public let expiresAt: Date
|
|
|
|
init(client: APIClient, teamId: String, json: [String: Any]) {
|
|
self.client = client
|
|
self.teamId = teamId
|
|
self.id = json["id"] as? String ?? ""
|
|
self.recipientEmail = json["recipient_email"] as? String
|
|
|
|
let millis = json["expires_at_millis"] as? Int64 ?? 0
|
|
self.expiresAt = Date(timeIntervalSince1970: Double(millis) / 1000.0)
|
|
}
|
|
|
|
public func revoke() async throws {
|
|
_ = try await client.sendRequest(
|
|
path: "/teams/\(teamId)/invitations/\(id)",
|
|
method: "DELETE",
|
|
authenticated: true
|
|
)
|
|
}
|
|
}
|