Add managed content and keyboard usage insights
CI / verify (push) Has been cancelled
CI / publish (push) Has been cancelled

Introduce versioned official content workflows and privacy-safe keyboard analytics, while preventing repeat DeviceCheck sign-ins from incorrectly restricting eligible accounts.
This commit is contained in:
Rocky
2026-08-21 13:34:03 +08:00
parent b5212dcdc2
commit d0abe27623
55 changed files with 4690 additions and 111 deletions
@@ -68,6 +68,11 @@ import com.osglab.account.features.credits.routes.creditRoutes
import com.osglab.account.features.credits.services.CreditOperations
import com.osglab.account.features.credits.services.CreditService
import com.osglab.account.features.credits.services.ReferralRewardConfig
import com.osglab.account.features.credits.services.signupTrialIdempotencyKey
import com.osglab.account.features.content.repositories.ContentRepository
import com.osglab.account.features.content.repositories.ExposedContentRepository
import com.osglab.account.features.content.routes.contentRoutes
import com.osglab.account.features.content.services.ContentService
import com.osglab.account.features.gateway.adapters.CreditReservationAdapter
import com.osglab.account.features.gateway.adapters.SessionIdentityAdapter
import com.osglab.account.features.gateway.GatewaySettings
@@ -328,6 +333,7 @@ fun Application.module() {
analyticsRoutes(koin.get())
configureInviteWebRoutes(koin.get(), koin.get(), koin.get())
integrityRoutes(koin.get())
contentRoutes(koin.get())
}
if (appConfig.admin.enabled) {
adminWebRoutes(appConfig)
@@ -342,6 +348,7 @@ fun Application.module() {
grantService = koin.get(),
operatorService = koin.get(),
auditService = koin.get(),
contentService = koin.get(),
)
}
}
@@ -417,6 +424,8 @@ fun accountServerModule(config: AppConfig): Module = module {
single<AdminUsersRepository> { ExposedAdminUsersRepository(get()) }
single { AdminUsersService(get()) }
single { AdminGrantService(get()) }
single<ContentRepository> { ExposedContentRepository(get()) }
single { ContentService(get()) }
single<AppleJwksProvider> {
RemoteAppleJwksProvider(get(), config.apple.jwksUrl)
}
@@ -486,12 +495,18 @@ fun accountServerModule(config: AppConfig): Module = module {
)
}
single<TrialCreditGranter> {
TrialCreditGranter { accountId ->
get<CreditService>().grantSignupTrial(
userId = accountId,
credits = config.credits.signupTrial,
idempotencyKey = "internal:signup-trial:$accountId",
)
val creditService = get<CreditService>()
object : TrialCreditGranter {
override suspend fun grant(accountId: UUID) {
creditService.grantSignupTrial(
userId = accountId,
credits = config.credits.signupTrial,
idempotencyKey = signupTrialIdempotencyKey(accountId),
)
}
override suspend fun wasGranted(accountId: UUID): Boolean =
creditService.hasSignupTrial(accountId)
}
}
single {
@@ -510,8 +525,8 @@ fun accountServerModule(config: AppConfig): Module = module {
single<ComplimentaryRequestPort> { get<ExposedGatewayRepository>() }
single<AccountProvisioner> {
AccountProvisioner { accountId, deviceCheckToken, displayName ->
val granted = get<DeviceCheckTrialService>().claimAndGrant(accountId, deviceCheckToken)
if (deviceCheckToken != null && !granted) {
val trial = get<DeviceCheckTrialService>().claimAndGrant(accountId, deviceCheckToken)
if (trial.shouldRestrictAccount) {
get<AuthRepository>().restrictAccountForAntiAbuse(
accountId,
java.time.Instant.now(),