@codingfactory/socialkit-vue 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/services/circles.d.ts +470 -0
- package/dist/services/circles.d.ts.map +1 -0
- package/dist/services/circles.js +1924 -0
- package/dist/services/circles.js.map +1 -0
- package/dist/services/identity.d.ts +29 -0
- package/dist/services/identity.d.ts.map +1 -0
- package/dist/services/identity.js +150 -0
- package/dist/services/identity.js.map +1 -0
- package/dist/stores/auth.d.ts +3 -0
- package/dist/stores/auth.d.ts.map +1 -1
- package/dist/stores/circles.d.ts +4283 -0
- package/dist/stores/circles.d.ts.map +1 -0
- package/dist/stores/circles.js +1670 -0
- package/dist/stores/circles.js.map +1 -0
- package/dist/types/api.d.ts +19 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/auth.d.ts +34 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +8 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/types/identity.d.ts +34 -0
- package/dist/types/identity.d.ts.map +1 -0
- package/dist/types/identity.js +5 -0
- package/dist/types/identity.js.map +1 -0
- package/dist/types/user.d.ts +1 -0
- package/dist/types/user.d.ts.map +1 -1
- package/dist/types/user.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +105 -0
- package/src/services/circles.ts +2767 -0
- package/src/services/identity.ts +281 -0
- package/src/stores/circles.ts +2114 -0
- package/src/types/api.ts +20 -0
- package/src/types/auth.ts +39 -0
- package/src/types/identity.ts +41 -0
- package/src/types/user.ts +1 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configurable identity service for SocialKit-powered frontends.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { AxiosInstance } from 'axios'
|
|
6
|
+
import type { TokenStorage } from '../utils/tokenStorage.js'
|
|
7
|
+
import { extractTokenFromResponse } from '../utils/tokenStorage.js'
|
|
8
|
+
import type {
|
|
9
|
+
AccountDeletionConfirmationResult,
|
|
10
|
+
AccountDeletionRequestResult,
|
|
11
|
+
ChangePasswordPayload,
|
|
12
|
+
ChangePasswordResult,
|
|
13
|
+
TwoFactorDisablePayload,
|
|
14
|
+
TwoFactorEnableResult,
|
|
15
|
+
TwoFactorEnrollment,
|
|
16
|
+
TwoFactorStatus
|
|
17
|
+
} from '../types/identity.js'
|
|
18
|
+
import type { IdentityUser } from '../types/user.js'
|
|
19
|
+
|
|
20
|
+
interface IdentityUserResponse {
|
|
21
|
+
id: string
|
|
22
|
+
handle?: string
|
|
23
|
+
name: string
|
|
24
|
+
avatar?: string | { url: string; thumb_url?: string } | null
|
|
25
|
+
avatar_url?: string | null
|
|
26
|
+
cover_photo?: string | null
|
|
27
|
+
bio?: string | null
|
|
28
|
+
email?: string
|
|
29
|
+
created_at?: string
|
|
30
|
+
updated_at?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface DataEnvelope<T> {
|
|
34
|
+
data?: T
|
|
35
|
+
message?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface TwoFactorStatusPayload {
|
|
39
|
+
enabled?: unknown
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface TwoFactorEnrollmentPayload {
|
|
43
|
+
qr_code_url?: unknown
|
|
44
|
+
secret_base32?: unknown
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface RecoveryCodesPayload {
|
|
48
|
+
recovery_codes?: unknown
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface AccountDeletionRequestPayload {
|
|
52
|
+
id?: unknown
|
|
53
|
+
status?: unknown
|
|
54
|
+
confirmation_token?: unknown
|
|
55
|
+
dev_token?: unknown
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface AccountDeletionConfirmationPayload {
|
|
59
|
+
status?: unknown
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface IdentityServiceConfig {
|
|
63
|
+
client: AxiosInstance
|
|
64
|
+
tokenStorage: TokenStorage
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface IdentityServiceInstance {
|
|
68
|
+
getCurrentUser(): Promise<IdentityUser>
|
|
69
|
+
getUser(userId: string): Promise<IdentityUser>
|
|
70
|
+
updateUser(userId: string, payload: Partial<IdentityUser>): Promise<IdentityUser>
|
|
71
|
+
uploadAvatar(file: File): Promise<{ url: string }>
|
|
72
|
+
changePassword(payload: ChangePasswordPayload): Promise<ChangePasswordResult>
|
|
73
|
+
getTwoFactorStatus(): Promise<TwoFactorStatus>
|
|
74
|
+
startTwoFactorEnrollment(currentPassword: string): Promise<TwoFactorEnrollment>
|
|
75
|
+
enableTwoFactor(code: string): Promise<TwoFactorEnableResult>
|
|
76
|
+
regenerateTwoFactorRecoveryCodes(code: string): Promise<TwoFactorEnableResult>
|
|
77
|
+
disableTwoFactor(payload: TwoFactorDisablePayload): Promise<void>
|
|
78
|
+
requestAccountDeletion(): Promise<AccountDeletionRequestResult>
|
|
79
|
+
confirmAccountDeletion(
|
|
80
|
+
requestId: string,
|
|
81
|
+
confirmationToken: string
|
|
82
|
+
): Promise<AccountDeletionConfirmationResult>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class IdentityService implements IdentityServiceInstance {
|
|
86
|
+
public constructor(
|
|
87
|
+
private readonly client: AxiosInstance,
|
|
88
|
+
private readonly tokenStorage: TokenStorage
|
|
89
|
+
) {}
|
|
90
|
+
|
|
91
|
+
public async getCurrentUser(): Promise<IdentityUser> {
|
|
92
|
+
const response = await this.client.get<DataEnvelope<unknown>>('/v1/me')
|
|
93
|
+
return this.normalizeUser(response.data.data)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public async getUser(userId: string): Promise<IdentityUser> {
|
|
97
|
+
const response = await this.client.get<DataEnvelope<unknown>>('/v1/identity/users/by-ids', {
|
|
98
|
+
params: {
|
|
99
|
+
ids: userId
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const users = Array.isArray(response.data?.data) ? response.data.data : []
|
|
104
|
+
const user = users.find((candidate: unknown) => {
|
|
105
|
+
if (!candidate || typeof candidate !== 'object') {
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const candidateWithId = candidate as { id?: unknown }
|
|
110
|
+
|
|
111
|
+
return candidateWithId.id === userId
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
if (!user) {
|
|
115
|
+
throw new Error('User not found')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return this.normalizeUser(user)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public async updateUser(
|
|
122
|
+
_userId: string,
|
|
123
|
+
payload: Partial<IdentityUser>
|
|
124
|
+
): Promise<IdentityUser> {
|
|
125
|
+
const response = await this.client.patch<DataEnvelope<unknown>>('/v1/me', payload)
|
|
126
|
+
return this.normalizeUser(response.data.data)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public async uploadAvatar(file: File): Promise<{ url: string }> {
|
|
130
|
+
const formData = new FormData()
|
|
131
|
+
formData.append('file', file)
|
|
132
|
+
|
|
133
|
+
const response = await this.client.post<{ url: string }>('/v1/media/upload', formData)
|
|
134
|
+
return response.data
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public async changePassword(payload: ChangePasswordPayload): Promise<ChangePasswordResult> {
|
|
138
|
+
const response = await this.client.patch<DataEnvelope<unknown>>('/v1/me/password', {
|
|
139
|
+
current_password: payload.currentPassword,
|
|
140
|
+
password: payload.newPassword,
|
|
141
|
+
password_confirmation: payload.confirmPassword
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const rotatedToken = extractTokenFromResponse(response.data)
|
|
145
|
+
if (rotatedToken) {
|
|
146
|
+
this.tokenStorage.setToken(rotatedToken)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const message = typeof response.data?.message === 'string' && response.data.message.trim() !== ''
|
|
150
|
+
? response.data.message
|
|
151
|
+
: 'Password updated successfully'
|
|
152
|
+
|
|
153
|
+
return { message }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public async getTwoFactorStatus(): Promise<TwoFactorStatus> {
|
|
157
|
+
const response = await this.client.get<DataEnvelope<TwoFactorStatusPayload>>('/v1/security/2fa/status')
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
enabled: response.data?.data?.enabled === true
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public async startTwoFactorEnrollment(currentPassword: string): Promise<TwoFactorEnrollment> {
|
|
165
|
+
const response = await this.client.post<DataEnvelope<TwoFactorEnrollmentPayload>>(
|
|
166
|
+
'/v1/security/2fa/start',
|
|
167
|
+
{
|
|
168
|
+
current_password: currentPassword
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
const qrCodeUrl = response.data?.data?.qr_code_url
|
|
173
|
+
const secretBase32 = response.data?.data?.secret_base32
|
|
174
|
+
|
|
175
|
+
if (typeof qrCodeUrl !== 'string' || typeof secretBase32 !== 'string') {
|
|
176
|
+
throw new Error('Invalid two-factor enrollment response payload')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
qrCodeUrl,
|
|
181
|
+
secretBase32
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async enableTwoFactor(code: string): Promise<TwoFactorEnableResult> {
|
|
186
|
+
const response = await this.client.post<DataEnvelope<RecoveryCodesPayload>>(
|
|
187
|
+
'/v1/security/2fa/enable',
|
|
188
|
+
{ code }
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
recoveryCodes: this.normalizeRecoveryCodes(response.data?.data?.recovery_codes)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public async regenerateTwoFactorRecoveryCodes(code: string): Promise<TwoFactorEnableResult> {
|
|
197
|
+
const response = await this.client.post<DataEnvelope<RecoveryCodesPayload>>(
|
|
198
|
+
'/v1/security/2fa/recovery-codes/regenerate',
|
|
199
|
+
{ code }
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
recoveryCodes: this.normalizeRecoveryCodes(response.data?.data?.recovery_codes)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public async disableTwoFactor(payload: TwoFactorDisablePayload): Promise<void> {
|
|
208
|
+
await this.client.post('/v1/security/2fa/disable', {
|
|
209
|
+
password: payload.password,
|
|
210
|
+
code: payload.code
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public async requestAccountDeletion(): Promise<AccountDeletionRequestResult> {
|
|
215
|
+
const response = await this.client.post<DataEnvelope<AccountDeletionRequestPayload>>('/v1/compliance/dsar/erase')
|
|
216
|
+
const id = response.data?.data?.id
|
|
217
|
+
const status = response.data?.data?.status
|
|
218
|
+
const confirmationToken = response.data?.data?.confirmation_token
|
|
219
|
+
const devToken = response.data?.data?.dev_token
|
|
220
|
+
|
|
221
|
+
if (typeof id !== 'string' || typeof status !== 'string') {
|
|
222
|
+
throw new Error('Invalid account deletion request payload')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
requestId: id,
|
|
227
|
+
status,
|
|
228
|
+
confirmationToken: typeof confirmationToken === 'string'
|
|
229
|
+
? confirmationToken
|
|
230
|
+
: typeof devToken === 'string'
|
|
231
|
+
? devToken
|
|
232
|
+
: null
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
public async confirmAccountDeletion(
|
|
237
|
+
requestId: string,
|
|
238
|
+
confirmationToken: string
|
|
239
|
+
): Promise<AccountDeletionConfirmationResult> {
|
|
240
|
+
const response = await this.client.post<DataEnvelope<AccountDeletionConfirmationPayload>>(
|
|
241
|
+
`/v1/compliance/dsar/${encodeURIComponent(requestId)}/confirm`,
|
|
242
|
+
{
|
|
243
|
+
token: confirmationToken
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
const status = response.data?.data?.status
|
|
247
|
+
|
|
248
|
+
if (typeof status !== 'string') {
|
|
249
|
+
throw new Error('Invalid account deletion confirmation payload')
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return { status }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private normalizeUser(user: unknown): IdentityUser {
|
|
256
|
+
if (!user || typeof user !== 'object') {
|
|
257
|
+
throw new Error('Invalid user data')
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const userData = user as IdentityUserResponse
|
|
261
|
+
const avatarUrl = typeof userData.avatar === 'string'
|
|
262
|
+
? userData.avatar
|
|
263
|
+
: userData.avatar?.url ?? userData.avatar_url ?? null
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
...userData,
|
|
267
|
+
avatar: avatarUrl,
|
|
268
|
+
...(avatarUrl ? { avatar_url: avatarUrl } : {})
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private normalizeRecoveryCodes(recoveryCodesRaw: unknown): string[] {
|
|
273
|
+
return Array.isArray(recoveryCodesRaw)
|
|
274
|
+
? recoveryCodesRaw.filter((value: unknown): value is string => typeof value === 'string')
|
|
275
|
+
: []
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function createIdentityService(config: IdentityServiceConfig): IdentityServiceInstance {
|
|
280
|
+
return new IdentityService(config.client, config.tokenStorage)
|
|
281
|
+
}
|