basic types and identifier implementation
This commit is contained in:
@@ -6,6 +6,7 @@ kotlinx-html = "0.12.0"
|
||||
kotlinx-browser = "0.5.0"
|
||||
kotlinx-serialization = "1.8.0"
|
||||
kotlin-css-jvm = "2025.6.4"
|
||||
kotlinx-datetime = "0.7.1"
|
||||
ktor = "3.4.1"
|
||||
ktor-server-rate-limiting = "2.2.1"
|
||||
logback = "1.5.13"
|
||||
@@ -25,6 +26,7 @@ kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotl
|
||||
kotlinx-html = { module = "org.jetbrains.kotlinx:kotlinx-html", version.ref = "kotlinx-html" }
|
||||
kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinx-browser" }
|
||||
kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
|
||||
kotlin-css = { module = "org.jetbrains.kotlin-wrappers:kotlin-css-jvm", version.ref = "kotlin-css-jvm" }
|
||||
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
||||
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
|
||||
@@ -35,7 +37,7 @@ kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version
|
||||
[bundles]
|
||||
jvmMain = ["ktor-server-core", "ktor-server-netty", "ktor-server-auth", "ktor-server-content-negotiation", "ktor-serialization-kotlinx-json", "ktor-server-sessions", "ktor-server-host-common", "ktor-server-html-builder", "ktor-server-rate-limiting", "kotlinx-html", "kotlin-css", "exposed-core", "exposed-jdbc", "h2"]
|
||||
jvmTest = ["ktor-test", "kotlin-test-junit"]
|
||||
commonMain = ["kotlinx-serialization"]
|
||||
commonMain = ["kotlinx-serialization", "kotlinx-datetime"]
|
||||
commonTest = ["kotlin-test"]
|
||||
jsMain = ["kotlinx-browser"]
|
||||
jsTest = []
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
package online.maestoso.cofront
|
||||
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
data class Identifier(val id: Uuid, val domain: String) {
|
||||
override fun toString(): String {
|
||||
return "$id@$domain"
|
||||
}
|
||||
}
|
||||
fun String.toIdentifier(): Identifier {
|
||||
val split = split('@', limit = 2)
|
||||
return Identifier(Uuid.parse(split[0]), split[1])
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package online.maestoso.cofront.payloads
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import online.maestoso.cofront.types.Member
|
||||
|
||||
@Serializable
|
||||
data class GetMemberPayload(val member: Member)
|
||||
@@ -0,0 +1,26 @@
|
||||
package online.maestoso.cofront.payloads
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import online.maestoso.cofront.types.Identifier
|
||||
import online.maestoso.cofront.types.JID
|
||||
import online.maestoso.cofront.types.Member
|
||||
|
||||
@Serializable
|
||||
data class SystemLookupPayload(
|
||||
/**
|
||||
* The list of current fronters.
|
||||
*/
|
||||
val fronters: Set<Identifier>,
|
||||
/**
|
||||
* The list of all members.
|
||||
*/
|
||||
val members: Set<Identifier>,
|
||||
/**
|
||||
* The list of all JIDs associated with this system.
|
||||
*/
|
||||
val jids: Set<JID>,
|
||||
/**
|
||||
* URL to a dashboard of more information to this system.
|
||||
*/
|
||||
val url: String,
|
||||
)
|
||||
@@ -0,0 +1,73 @@
|
||||
package online.maestoso.cofront.types
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import online.maestoso.cofront.util.toByteArray
|
||||
import online.maestoso.cofront.util.toUInt
|
||||
import online.maestoso.cofront.util.toULong
|
||||
import kotlin.io.encoding.Base64
|
||||
|
||||
@Serializable(with = IdentifierSerializer::class)
|
||||
data class Identifier(val system: SystemId, val member: MemberId?, val domain: String) {
|
||||
data class MemberId(val serial: ULong, val time: ULong, val id: ULong)
|
||||
data class SystemId(val site: UInt, val host: UInt, val time: ULong, val id: ULong)
|
||||
}
|
||||
object IdentifierSerializer : KSerializer<Identifier> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("memberid", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(
|
||||
encoder: Encoder,
|
||||
value: Identifier
|
||||
) {
|
||||
val buf = ByteArray(48) {0}
|
||||
value.system.site.toByteArray().copyInto(buf, 0)
|
||||
value.system.host.toByteArray().copyInto(buf, 4)
|
||||
value.system.time.toByteArray().copyInto(buf, 8)
|
||||
value.system.id.toByteArray().copyInto(buf, 16)
|
||||
if (value.member != null) {
|
||||
value.member.serial.toByteArray().copyInto(buf, 24)
|
||||
value.member.time.toByteArray().copyInto(buf, 32)
|
||||
value.member.id.toByteArray().copyInto(buf, 40)
|
||||
} else {
|
||||
buf.dropLast(24)
|
||||
}
|
||||
encoder.encodeString(Base64.UrlSafe.encode(buf) + ":${value.domain}")
|
||||
}
|
||||
@Throws(IllegalArgumentException::class)
|
||||
override fun deserialize(decoder: Decoder): Identifier {
|
||||
val str = decoder.decodeString()
|
||||
val i = str.indexOfFirst {it == ':'}
|
||||
if(i != 24 || i != 48 || i == -1) throw IllegalArgumentException("colon not correctly placed")
|
||||
val id = str.substring(0..<i)
|
||||
val domain = str.substring(i..<str.length)
|
||||
val buf = Base64.UrlSafe.decode(id)
|
||||
val member: Identifier.MemberId? = when (buf.size) {
|
||||
24 -> {
|
||||
null
|
||||
}
|
||||
48 -> {
|
||||
Identifier.MemberId(
|
||||
buf.copyOfRange(24, 31).toULong(),
|
||||
buf.copyOfRange(32, 39).toULong(),
|
||||
buf.copyOfRange(40, 47).toULong()
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("identifier was the wrong size: expected 24 or 48 bytes, got ${buf.size}")
|
||||
}
|
||||
return Identifier(
|
||||
Identifier.SystemId(
|
||||
buf.copyOfRange(0, 3).toUInt(),
|
||||
buf.copyOfRange(4, 7).toUInt(),
|
||||
buf.copyOfRange(8, 15).toULong(),
|
||||
buf.copyOfRange(16, 23).toULong(),
|
||||
),
|
||||
member,
|
||||
domain
|
||||
)
|
||||
}
|
||||
}
|
||||
29
src/commonMain/kotlin/online/maestoso/cofront/types/JID.kt
Normal file
29
src/commonMain/kotlin/online/maestoso/cofront/types/JID.kt
Normal file
@@ -0,0 +1,29 @@
|
||||
package online.maestoso.cofront.types
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@Serializable(with = JIDSerializer::class)
|
||||
data class JID(val node: String, val domain: String, val resource: String) {
|
||||
override fun toString(): String {
|
||||
return "$node@$domain/$resource"
|
||||
}
|
||||
}
|
||||
class JIDSerializer : KSerializer<JID> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("jid", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: JID) {
|
||||
encoder.encodeString(value.toString())
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): JID {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package online.maestoso.cofront.types
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Serializable
|
||||
data class Member(
|
||||
/**
|
||||
Unique identifier for this member.
|
||||
*/
|
||||
val id: Identifier,
|
||||
/**
|
||||
Short but human-readable name for this member.
|
||||
*/
|
||||
val shortname: String,
|
||||
/**
|
||||
* Creation date and time for this member.
|
||||
*/
|
||||
val created: Instant,
|
||||
/**
|
||||
* The profile associated with this member.
|
||||
*/
|
||||
val profile: Profile,
|
||||
/**
|
||||
* A set of descriptions of how this member is related to other members in the system.
|
||||
* This can include a variety of implementation-specific information, such as groups,
|
||||
* a "root member" for a system profile, or represent interpersonal relationships.
|
||||
*/
|
||||
val relations: Set<MemberRelation>,
|
||||
/**
|
||||
* Endpoint to look up fronter/full system information.
|
||||
*/
|
||||
val system: String,
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package online.maestoso.cofront.types
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MemberRelation(val from: Identifier, val to: Identifier, val type: String)
|
||||
@@ -0,0 +1,29 @@
|
||||
package online.maestoso.cofront.types
|
||||
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Instant
|
||||
|
||||
@Serializable
|
||||
data class Profile(
|
||||
/**
|
||||
* Long display name for this profile.
|
||||
*/
|
||||
val name: String?,
|
||||
/**
|
||||
* Profile's pronouns.
|
||||
*/
|
||||
val pronouns: String?,
|
||||
/**
|
||||
* Profile's preferred accent color.
|
||||
*/
|
||||
val color: String?,
|
||||
/**
|
||||
* URL to access profile picture.
|
||||
*/
|
||||
val avatar: String?,
|
||||
/**
|
||||
* This member's preferred birthday.
|
||||
*/
|
||||
val birthday: LocalDateTime?,
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
package online.maestoso.cofront.util
|
||||
|
||||
fun UInt.toByteArray(): ByteArray {
|
||||
val b = ByteArray(4) {0}
|
||||
b[0] = this.toByte()
|
||||
b[1] = (this shr 8).toByte()
|
||||
b[2] = (this shr 16).toByte()
|
||||
b[3] = (this shr 24).toByte()
|
||||
return b
|
||||
}
|
||||
|
||||
fun ByteArray.toUInt(): UInt {
|
||||
var i = 0u
|
||||
i = i or this[0].toUInt()
|
||||
i = i or (this[1].toUInt() shl 8)
|
||||
i = i or (this[2].toUInt() shl 16)
|
||||
i = i or (this[3].toUInt() shl 24)
|
||||
return i
|
||||
}
|
||||
|
||||
fun ULong.toByteArray(): ByteArray {
|
||||
val b = ByteArray(8) {0}
|
||||
b[0] = this.toByte()
|
||||
b[1] = (this shr 8).toByte()
|
||||
b[2] = (this shr 16).toByte()
|
||||
b[3] = (this shr 24).toByte()
|
||||
b[4] = (this shr 32).toByte()
|
||||
b[5] = (this shr 40).toByte()
|
||||
b[6] = (this shr 48).toByte()
|
||||
b[7] = (this shr 56).toByte()
|
||||
return b
|
||||
}
|
||||
|
||||
fun ByteArray.toULong(): ULong {
|
||||
var i = 0UL
|
||||
i = i or this[0].toULong()
|
||||
i = i or (this[1].toULong() shl 8)
|
||||
i = i or (this[2].toULong() shl 16)
|
||||
i = i or (this[3].toULong() shl 24)
|
||||
i = i or (this[4].toULong() shl 32)
|
||||
i = i or (this[5].toULong() shl 40)
|
||||
i = i or (this[6].toULong() shl 48)
|
||||
i = i or (this[7].toULong() shl 56)
|
||||
return i
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package online.maestoso.cofront.util
|
||||
|
||||
fun String.isValidDomain(): Boolean {
|
||||
// regex stolen from https://stackoverflow.com/a/3824105. if it's broken its not my fault
|
||||
val regex = """^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$"""
|
||||
.toRegex()
|
||||
return regex.matches(this)
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
|
||||
import kotlinx.browser.window
|
||||
import online.maestoso.cofront.Identifier
|
||||
import online.maestoso.cofront.types.Identifier
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
fun main() {
|
||||
window.alert("kotlin js test")
|
||||
val i = Identifier(Uuid.generateV7(), "localhost")
|
||||
}
|
||||
@@ -1,11 +1 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
|
||||
package online.maestoso.cofront.api
|
||||
|
||||
import online.maestoso.cofront.Identifier
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
fun test() {
|
||||
val i = Identifier(Uuid.generateV7(), "localhost")
|
||||
}
|
||||
Reference in New Issue
Block a user