refactor(auth): use Effect Schema internally
Model auth entries with Effect Schema inside AuthService and use Schema decoding when reading persisted auth data. Keep the Auth facade on Zod at the boundary so existing validators and callers stay stable during the migration.
This commit is contained in:
parent
f7259617e5
commit
7f12976ea0
2 changed files with 60 additions and 49 deletions
|
|
@ -1,27 +1,43 @@
|
|||
import { Effect } from "effect"
|
||||
import z from "zod"
|
||||
import { runtime } from "@/effect/runtime"
|
||||
import {
|
||||
Api as ApiSchema,
|
||||
AuthService,
|
||||
type AuthServiceError,
|
||||
Info as InfoSchema,
|
||||
Oauth as OauthSchema,
|
||||
WellKnown as WellKnownSchema,
|
||||
type Info as AuthInfo,
|
||||
} from "./service"
|
||||
import * as S from "./service"
|
||||
|
||||
export { OAUTH_DUMMY_KEY } from "./service"
|
||||
|
||||
function runPromise<A>(f: (service: AuthService.Service) => Effect.Effect<A, AuthServiceError>) {
|
||||
return runtime.runPromise(AuthService.use(f))
|
||||
function runPromise<A>(f: (service: S.AuthService.Service) => Effect.Effect<A, S.AuthServiceError>) {
|
||||
return runtime.runPromise(S.AuthService.use(f))
|
||||
}
|
||||
|
||||
export namespace Auth {
|
||||
export const Oauth = OauthSchema
|
||||
export const Api = ApiSchema
|
||||
export const WellKnown = WellKnownSchema
|
||||
export const Info = InfoSchema
|
||||
export type Info = AuthInfo
|
||||
export const Oauth = z
|
||||
.object({
|
||||
type: z.literal("oauth"),
|
||||
refresh: z.string(),
|
||||
access: z.string(),
|
||||
expires: z.number(),
|
||||
accountId: z.string().optional(),
|
||||
enterpriseUrl: z.string().optional(),
|
||||
})
|
||||
.meta({ ref: "OAuth" })
|
||||
|
||||
export const Api = z
|
||||
.object({
|
||||
type: z.literal("api"),
|
||||
key: z.string(),
|
||||
})
|
||||
.meta({ ref: "ApiAuth" })
|
||||
|
||||
export const WellKnown = z
|
||||
.object({
|
||||
type: z.literal("wellknown"),
|
||||
key: z.string(),
|
||||
token: z.string(),
|
||||
})
|
||||
.meta({ ref: "WellKnownAuth" })
|
||||
|
||||
export const Info = z.discriminatedUnion("type", [Oauth, Api, WellKnown]).meta({ ref: "Auth" })
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
export async function get(providerID: string) {
|
||||
return runPromise((service) => service.get(providerID))
|
||||
|
|
|
|||
|
|
@ -1,39 +1,32 @@
|
|||
import path from "path"
|
||||
import { Effect, Layer, Schema, ServiceMap } from "effect"
|
||||
import z from "zod"
|
||||
import { Effect, Layer, Option, Schema, ServiceMap } from "effect"
|
||||
import { Global } from "../global"
|
||||
import { Filesystem } from "../util/filesystem"
|
||||
|
||||
export const OAUTH_DUMMY_KEY = "opencode-oauth-dummy-key"
|
||||
|
||||
export const Oauth = z
|
||||
.object({
|
||||
type: z.literal("oauth"),
|
||||
refresh: z.string(),
|
||||
access: z.string(),
|
||||
expires: z.number(),
|
||||
accountId: z.string().optional(),
|
||||
enterpriseUrl: z.string().optional(),
|
||||
})
|
||||
.meta({ ref: "OAuth" })
|
||||
export class Oauth extends Schema.Class<Oauth>("OAuth")({
|
||||
type: Schema.Literal("oauth"),
|
||||
refresh: Schema.String,
|
||||
access: Schema.String,
|
||||
expires: Schema.Number,
|
||||
accountId: Schema.optional(Schema.String),
|
||||
enterpriseUrl: Schema.optional(Schema.String),
|
||||
}) {}
|
||||
|
||||
export const Api = z
|
||||
.object({
|
||||
type: z.literal("api"),
|
||||
key: z.string(),
|
||||
})
|
||||
.meta({ ref: "ApiAuth" })
|
||||
export class Api extends Schema.Class<Api>("ApiAuth")({
|
||||
type: Schema.Literal("api"),
|
||||
key: Schema.String,
|
||||
}) {}
|
||||
|
||||
export const WellKnown = z
|
||||
.object({
|
||||
type: z.literal("wellknown"),
|
||||
key: z.string(),
|
||||
token: z.string(),
|
||||
})
|
||||
.meta({ ref: "WellKnownAuth" })
|
||||
export class WellKnown extends Schema.Class<WellKnown>("WellKnownAuth")({
|
||||
type: Schema.Literal("wellknown"),
|
||||
key: Schema.String,
|
||||
token: Schema.String,
|
||||
}) {}
|
||||
|
||||
export const Info = z.discriminatedUnion("type", [Oauth, Api, WellKnown]).meta({ ref: "Auth" })
|
||||
export type Info = z.infer<typeof Info>
|
||||
export const Info = Schema.Union([Oauth, Api, WellKnown])
|
||||
export type Info = Schema.Schema.Type<typeof Info>
|
||||
|
||||
export class AuthServiceError extends Schema.TaggedErrorClass<AuthServiceError>()("AuthServiceError", {
|
||||
message: Schema.String,
|
||||
|
|
@ -57,15 +50,17 @@ export class AuthService extends ServiceMap.Service<AuthService, AuthService.Ser
|
|||
static readonly layer = Layer.effect(
|
||||
AuthService,
|
||||
Effect.gen(function* () {
|
||||
const decode = Schema.decodeUnknownOption(Info)
|
||||
|
||||
const all = Effect.fn("AuthService.all")(() =>
|
||||
Effect.tryPromise({
|
||||
try: async () => {
|
||||
const data = await Filesystem.readJson<Record<string, unknown>>(file).catch(() => ({}))
|
||||
return Object.entries(data).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const parsed = Info.safeParse(value)
|
||||
if (!parsed.success) return acc
|
||||
acc[key] = parsed.data
|
||||
const parsed = decode(value)
|
||||
if (Option.isNone(parsed)) return acc
|
||||
acc[key] = parsed.value
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, Info>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue