feat: migrate auth from supabase to better auth (#596)
* feat: add README for @cio/db package and remove existingAccounts.json * load session, load profile and organization, add create profile hook * chore: cache session request The trick was passing all the cookies to the backend. Previously I was only passing the better-auth.session_token and not better-auth.session_data. Read about the difference here https://www.better-auth.com/docs/concepts/cookies#custom-cookies * feat: login, logout, onboarding, verify email, reset password. * feat: complete change email and email verification * feat: standardise how we make rpc calls and create org endpoints * fix ui errors * fix: handle potential undefined customerId and standardize avatar URL property * refactor: standardize custom domain handling and update telegram chat ID references * chore: add tailwind prefix to @cio/ui this fixed the media query issues because @cio/ui classes were overriding this dashboard tailwind build * chore: clean up org sidebar * chore: persist mode state * feat: implement order breadcrumbs * chore: upgrade to better auth 1.4 * chore: address common auth flow bugs and setup app for others to setup locally.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export * from './org.svelte';
|
||||
export * from './org-plan.svelte';
|
||||
@@ -0,0 +1,46 @@
|
||||
import { BaseApi, classroomio } from '$lib/utils/services/api';
|
||||
|
||||
import type { TNewOrganizationPlan } from '@cio/db/types';
|
||||
|
||||
/**
|
||||
* API class for organization plan operations
|
||||
*/
|
||||
class OrgPlanApi extends BaseApi {
|
||||
/**
|
||||
* Creates a new organization plan
|
||||
* @param params Organization plan creation parameters
|
||||
* @returns Created organization plan
|
||||
*/
|
||||
async createOrgPlan(params: TNewOrganizationPlan) {
|
||||
return this.execute({
|
||||
requestFn: () => classroomio.organization.plan.$post({ json: params }),
|
||||
logContext: 'creating organization plan'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an organization plan
|
||||
* @param params Update parameters (subscriptionId, payload)
|
||||
* @returns Updated organization plan
|
||||
*/
|
||||
async updateOrgPlan(params: { subscriptionId: string; payload: Record<string, unknown> }) {
|
||||
return this.execute({
|
||||
requestFn: () => classroomio.organization.plan.$put({ json: params }),
|
||||
logContext: 'updating organization plan'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels an organization plan
|
||||
* @param params Cancel parameters (subscriptionId, payload)
|
||||
* @returns Updated organization plan
|
||||
*/
|
||||
async cancelOrgPlan(params: { subscriptionId: string; payload: Record<string, unknown> }) {
|
||||
return this.execute({
|
||||
requestFn: () => classroomio.organization.plan.cancel.$post({ json: params }),
|
||||
logContext: 'canceling organization plan'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const orgPlanApi = new OrgPlanApi();
|
||||
@@ -0,0 +1,108 @@
|
||||
import { BaseApi, classroomio } from '$lib/utils/services/api';
|
||||
import type {
|
||||
CoursesByOrganizationSiteName,
|
||||
GetAudienceResponse,
|
||||
GetCoursesBySiteNameResponse,
|
||||
OrganizationAudience,
|
||||
OrganizationTeamMembers
|
||||
} from '../utils/types';
|
||||
|
||||
import type { GetTeamResponse } from '../utils/types';
|
||||
import type { TGetOrganizations } from '@cio/utils/validation/organization';
|
||||
import { orgs } from '$lib/utils/store/org';
|
||||
|
||||
/**
|
||||
* API class for organization operations
|
||||
*/
|
||||
class OrgApi extends BaseApi {
|
||||
teamMembers = $state<OrganizationTeamMembers>([]);
|
||||
audience = $state<OrganizationAudience>([]);
|
||||
orgSiteCourses = $state<CoursesByOrganizationSiteName>([]);
|
||||
|
||||
/**
|
||||
* Gets organization team members (non-students)
|
||||
* @param orgId Organization ID
|
||||
* @returns Team members array
|
||||
*/
|
||||
async getOrgTeam(orgId: string) {
|
||||
return this.execute<GetTeamResponse>({
|
||||
requestFn: () =>
|
||||
classroomio.organization[':orgId'].team.$get({
|
||||
param: { orgId }
|
||||
}),
|
||||
logContext: 'fetching organization team',
|
||||
onSuccess: (response) => {
|
||||
this.teamMembers = response.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets organization audience (students)
|
||||
* @param orgId Organization ID
|
||||
* @returns Audience array
|
||||
*/
|
||||
async getOrgAudience(orgId: string) {
|
||||
return this.execute<GetAudienceResponse>({
|
||||
requestFn: () =>
|
||||
classroomio.organization[':orgId'].audience.$get({
|
||||
param: { orgId }
|
||||
}),
|
||||
logContext: 'fetching organization audience',
|
||||
onSuccess: (response) => {
|
||||
this.audience = response.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets courses by organization siteName
|
||||
* @param siteName Organization site name
|
||||
* @returns Courses array
|
||||
*/
|
||||
async getCourseBySiteName(siteName: string) {
|
||||
return this.execute<GetCoursesBySiteNameResponse>({
|
||||
requestFn: () =>
|
||||
classroomio.organization.courses.$get({
|
||||
query: { siteName }
|
||||
}),
|
||||
logContext: 'fetching courses',
|
||||
onSuccess: (response) => {
|
||||
this.orgSiteCourses = response.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current organization by siteName or custom domain
|
||||
* @param siteName Organization site name or custom domain
|
||||
* @param isCustomDomain Whether the siteName is a custom domain
|
||||
* @returns Organization data or null if not found
|
||||
*/
|
||||
async getOrgBySiteName(siteName: string, isCustomDomain = false) {
|
||||
const query: TGetOrganizations = { siteName };
|
||||
if (isCustomDomain) {
|
||||
query.customDomain = siteName;
|
||||
query.isCustomDomainVerified = true;
|
||||
}
|
||||
|
||||
const result = await this.execute<typeof classroomio.organization.$get>({
|
||||
requestFn: () =>
|
||||
classroomio.organization.$get({
|
||||
query
|
||||
}),
|
||||
logContext: 'fetching organization',
|
||||
onSuccess: (response) => {
|
||||
if (!response.data || !Array.isArray(response.data) || response.data.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
orgs.set(response.data);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export const orgApi = new OrgApi();
|
||||
@@ -0,0 +1,25 @@
|
||||
import { classroomio, type InferResponseType } from '$lib/utils/services/api';
|
||||
|
||||
// Team member types
|
||||
export type GetTeamResponse = (typeof classroomio.organization)[':orgId']['team']['$get'];
|
||||
export type OrganizationTeamResponse = InferResponseType<GetTeamResponse> | null;
|
||||
export type OrganizationTeamSuccess = Extract<InferResponseType<GetTeamResponse>, { success: true }>;
|
||||
|
||||
export type OrganizationTeamMembers = OrganizationTeamSuccess['data'];
|
||||
|
||||
// Audience types
|
||||
export type GetAudienceResponse = (typeof classroomio.organization)[':orgId']['audience']['$get'];
|
||||
export type OrganizationAudienceResponse = InferResponseType<GetAudienceResponse> | null;
|
||||
export type OrganizationAudienceSuccess = Extract<InferResponseType<GetAudienceResponse>, { success: true }>;
|
||||
|
||||
export type OrganizationAudience = OrganizationAudienceSuccess['data'];
|
||||
|
||||
// Org Public Courses types
|
||||
export type GetCoursesBySiteNameResponse = typeof classroomio.organization.courses.$get;
|
||||
export type CoursesByOrganizationSiteNameResponse = InferResponseType<GetCoursesBySiteNameResponse> | null;
|
||||
export type CoursesByOrganizationSiteNameSuccess = Extract<
|
||||
InferResponseType<GetCoursesBySiteNameResponse>,
|
||||
{ success: true }
|
||||
>;
|
||||
|
||||
export type CoursesByOrganizationSiteName = CoursesByOrganizationSiteNameSuccess['data'];
|
||||
Reference in New Issue
Block a user