♻️ Transformed API keys
This commit is contained in:
@@ -10,7 +10,7 @@ import type { SnAttachment } from '~/types/api'
|
|||||||
|
|
||||||
const props = defineProps<{ item: SnAttachment }>()
|
const props = defineProps<{ item: SnAttachment }>()
|
||||||
|
|
||||||
const itemType = computed(() => props.item.mime_type.split('/')[0] ?? 'unknown')
|
const itemType = computed(() => props.item.mimeType.split('/')[0] ?? 'unknown')
|
||||||
|
|
||||||
const apiBase = useSolarNetworkUrl();
|
const apiBase = useSolarNetworkUrl();
|
||||||
const remoteSource = computed(() => `${apiBase}/drive/files/${props.item.id}?original=true`)
|
const remoteSource = computed(() => `${apiBase}/drive/files/${props.item.id}?original=true`)
|
||||||
|
@@ -7,9 +7,9 @@
|
|||||||
<span class="text-xs">@{{ props.item.publisher.name }}</span>
|
<span class="text-xs">@{{ props.item.publisher.name }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-xs flex gap-1">
|
<p class="text-xs flex gap-1">
|
||||||
<span>{{ DateTime.fromISO(props.item.created_at).toRelative() }}</span>
|
<span>{{ DateTime.fromISO(props.item.createdAt).toRelative() }}</span>
|
||||||
<span class="font-bold">·</span>
|
<span class="font-bold">·</span>
|
||||||
<span>{{ DateTime.fromISO(props.item.created_at).toLocaleString() }}</span>
|
<span>{{ DateTime.fromISO(props.item.createdAt).toLocaleString() }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,8 +18,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
|
import type { SnPost } from '~/types/api';
|
||||||
|
|
||||||
const props = defineProps<{ item: any }>()
|
const props = defineProps<{ item: SnPost }>()
|
||||||
|
|
||||||
const apiBase = useSolarNetworkUrl();
|
const apiBase = useSolarNetworkUrl();
|
||||||
const publisherAvatar = computed(() =>
|
const publisherAvatar = computed(() =>
|
||||||
|
@@ -1,10 +1,28 @@
|
|||||||
// Solar Network aka the api client
|
// Solar Network aka the api client
|
||||||
|
import { keysToCamel, keysToSnake } from '~/utils/transformKeys'
|
||||||
|
|
||||||
export const useSolarNetwork = () => {
|
export const useSolarNetwork = () => {
|
||||||
const apiBase = useSolarNetworkUrl();
|
const apiBase = useSolarNetworkUrl();
|
||||||
return $fetch.create({ baseURL: apiBase, credentials: 'include' })
|
|
||||||
|
return $fetch.create({
|
||||||
|
baseURL: apiBase,
|
||||||
|
credentials: 'include',
|
||||||
|
// Transform response keys from snake_case to camelCase
|
||||||
|
onResponse: ({ response }) => {
|
||||||
|
if (response._data) {
|
||||||
|
response._data = keysToCamel(response._data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Transform request data from camelCase to snake_case
|
||||||
|
onRequest: ({ options }) => {
|
||||||
|
if (options.body && typeof options.body === 'object') {
|
||||||
|
options.body = keysToSnake(options.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSolarNetworkUrl = () => {
|
export const useSolarNetworkUrl = () => {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
return config.public.apiBase
|
return config.public.apiBase
|
||||||
}
|
}
|
||||||
|
@@ -73,7 +73,7 @@ async function fetchActivites() {
|
|||||||
const resp = await api(
|
const resp = await api(
|
||||||
activitesLast.value == null
|
activitesLast.value == null
|
||||||
? '/sphere/activities'
|
? '/sphere/activities'
|
||||||
: `/sphere/activities?cursor=${new Date(activitesLast.value.created_at).toISOString()}`,
|
: `/sphere/activities?cursor=${new Date(activitesLast.value.createdAt).toISOString()}`,
|
||||||
)
|
)
|
||||||
const data = resp as SnActivity[]
|
const data = resp as SnActivity[]
|
||||||
activites.value = [...activites.value, ...data]
|
activites.value = [...activites.value, ...data]
|
||||||
|
@@ -4,11 +4,11 @@ import type { SnPost } from './post'
|
|||||||
export interface SnActivity {
|
export interface SnActivity {
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
resource_identifier: string;
|
resourceIdentifier: string;
|
||||||
meta: Record<string, unknown>;
|
meta: Record<string, unknown>;
|
||||||
data: SnPost;
|
data: SnPost;
|
||||||
visibility: number;
|
visibility: number;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
@@ -16,26 +16,26 @@ export interface SnFileMeta {
|
|||||||
yoffset?: number;
|
yoffset?: number;
|
||||||
filename?: string | null;
|
filename?: string | null;
|
||||||
orientation?: number;
|
orientation?: number;
|
||||||
'vips-loader'?: string;
|
vipsLoader?: string;
|
||||||
interpretation?: number;
|
interpretation?: number;
|
||||||
'bits-per-sample'?: number;
|
bitsPerSample?: number;
|
||||||
'resolution-unit'?: string;
|
resolutionUnit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attachment interface
|
// Attachment interface
|
||||||
export interface SnAttachment {
|
export interface SnAttachment {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
file_meta: SnFileMeta;
|
fileMeta: SnFileMeta;
|
||||||
user_meta: Record<string, unknown> | null;
|
userMeta: Record<string, unknown> | null;
|
||||||
sensitive_marks: string[];
|
sensitiveMarks: string[];
|
||||||
mime_type: string;
|
mimeType: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
size: number;
|
size: number;
|
||||||
has_compression: boolean;
|
hasCompression: boolean;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post interface
|
// Post interface
|
||||||
@@ -44,40 +44,40 @@ export interface SnPost {
|
|||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
slug: string | null;
|
slug: string | null;
|
||||||
edited_at: string | null;
|
editedAt: string | null;
|
||||||
published_at: string;
|
publishedAt: string;
|
||||||
visibility: number;
|
visibility: number;
|
||||||
content: string;
|
content: string;
|
||||||
type: number;
|
type: number;
|
||||||
pin_mode: unknown | null;
|
pinMode: unknown | null;
|
||||||
meta: unknown | null;
|
meta: unknown | null;
|
||||||
sensitive_marks: string[];
|
sensitiveMarks: string[];
|
||||||
embed_view: unknown | null;
|
embedView: unknown | null;
|
||||||
views_unique: number;
|
viewsUnique: number;
|
||||||
views_total: number;
|
viewsTotal: number;
|
||||||
upvotes: number;
|
upvotes: number;
|
||||||
downvotes: number;
|
downvotes: number;
|
||||||
awarded_score: number;
|
awardedScore: number;
|
||||||
reactions_count: Record<string, number>;
|
reactionsCount: Record<string, number>;
|
||||||
replies_count: number;
|
repliesCount: number;
|
||||||
reactions_made: Record<string, unknown>;
|
reactionsMade: Record<string, unknown>;
|
||||||
replied_gone: boolean;
|
repliedGone: boolean;
|
||||||
forwarded_gone: boolean;
|
forwardedGone: boolean;
|
||||||
replied_post_id: string | null;
|
repliedPostId: string | null;
|
||||||
replied_post: SnPost | null;
|
repliedPost: SnPost | null;
|
||||||
forwarded_post_id: string | null;
|
forwardedPostId: string | null;
|
||||||
forwarded_post: SnPost | null;
|
forwardedPost: SnPost | null;
|
||||||
realm_id: string | null;
|
realmId: string | null;
|
||||||
realm: unknown | null;
|
realm: unknown | null;
|
||||||
attachments: SnAttachment[];
|
attachments: SnAttachment[];
|
||||||
publisher_id: string;
|
publisherId: string;
|
||||||
publisher: SnPublisher;
|
publisher: SnPublisher;
|
||||||
awards: unknown | null;
|
awards: unknown | null;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
categories: string[];
|
categories: string[];
|
||||||
is_truncated: boolean;
|
isTruncated: boolean;
|
||||||
resource_identifier: string;
|
resourceIdentifier: string;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ export interface SnVerification {
|
|||||||
type: number;
|
type: number;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
verified_by: string;
|
verifiedBy: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publisher interface
|
// Publisher interface
|
||||||
@@ -15,16 +15,16 @@ export interface SnPublisher {
|
|||||||
name: string;
|
name: string;
|
||||||
nick: string;
|
nick: string;
|
||||||
bio: string;
|
bio: string;
|
||||||
picture_id: string;
|
pictureId: string;
|
||||||
background_id: string;
|
backgroundId: string;
|
||||||
picture: SnAttachment | null;
|
picture: SnAttachment | null;
|
||||||
background: SnAttachment | null;
|
background: SnAttachment | null;
|
||||||
verification: SnVerification | null;
|
verification: SnVerification | null;
|
||||||
account_id: string;
|
accountId: string;
|
||||||
realm_id: string | null;
|
realmId: string | null;
|
||||||
account: unknown | null;
|
account: unknown | null;
|
||||||
resource_identifier: string;
|
resourceIdentifier: string;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
@@ -14,62 +14,62 @@ export interface SnAccountBadge {
|
|||||||
label: string | null;
|
label: string | null;
|
||||||
caption: string | null;
|
caption: string | null;
|
||||||
meta: Record<string, unknown>;
|
meta: Record<string, unknown>;
|
||||||
activated_at: string;
|
activatedAt: string;
|
||||||
expired_at: string | null;
|
expiredAt: string | null;
|
||||||
account_id: string;
|
accountId: string;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account perk subscription interface
|
// Account perk subscription interface
|
||||||
export interface SnAccountPerkSubscription {
|
export interface SnAccountPerkSubscription {
|
||||||
id: string;
|
id: string;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
begun_at: string;
|
begunAt: string;
|
||||||
ended_at: string;
|
endedAt: string;
|
||||||
is_active: boolean;
|
isActive: boolean;
|
||||||
is_available: boolean;
|
isAvailable: boolean;
|
||||||
is_free_trial: boolean;
|
isFreeTrial: boolean;
|
||||||
status: number;
|
status: number;
|
||||||
base_price: number;
|
basePrice: number;
|
||||||
final_price: number;
|
finalPrice: number;
|
||||||
renewal_at: string;
|
renewalAt: string;
|
||||||
account_id: string;
|
accountId: string;
|
||||||
display_name: string;
|
displayName: string;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account profile interface
|
// Account profile interface
|
||||||
export interface SnAccountProfile {
|
export interface SnAccountProfile {
|
||||||
id: string;
|
id: string;
|
||||||
first_name: string;
|
firstName: string;
|
||||||
middle_name: string;
|
middleName: string;
|
||||||
last_name: string;
|
lastName: string;
|
||||||
bio: string;
|
bio: string;
|
||||||
gender: string;
|
gender: string;
|
||||||
pronouns: string;
|
pronouns: string;
|
||||||
time_zone: string;
|
timeZone: string;
|
||||||
location: string;
|
location: string;
|
||||||
links: SnAccountLink[];
|
links: SnAccountLink[];
|
||||||
birthday: string;
|
birthday: string;
|
||||||
last_seen_at: string;
|
lastSeenAt: string;
|
||||||
verification: SnVerification | null;
|
verification: SnVerification | null;
|
||||||
active_badge: unknown | null;
|
activeBadge: unknown | null;
|
||||||
experience: number;
|
experience: number;
|
||||||
level: number;
|
level: number;
|
||||||
social_credits: number;
|
socialCredits: number;
|
||||||
social_credits_level: number;
|
socialCreditsLevel: number;
|
||||||
leveling_progress: number;
|
levelingProgress: number;
|
||||||
picture: SnAttachment | null;
|
picture: SnAttachment | null;
|
||||||
background: SnAttachment | null;
|
background: SnAttachment | null;
|
||||||
account_id: string;
|
accountId: string;
|
||||||
resource_identifier: string;
|
resourceIdentifier: string;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account interface
|
// Account interface
|
||||||
@@ -79,14 +79,14 @@ export interface SnAccount {
|
|||||||
nick: string;
|
nick: string;
|
||||||
language: string;
|
language: string;
|
||||||
region: string;
|
region: string;
|
||||||
activated_at: string;
|
activatedAt: string;
|
||||||
is_superuser: boolean;
|
isSuperuser: boolean;
|
||||||
automated_id: string | null;
|
automatedId: string | null;
|
||||||
profile: SnAccountProfile;
|
profile: SnAccountProfile;
|
||||||
contacts: unknown[];
|
contacts: unknown[];
|
||||||
badges: SnAccountBadge[];
|
badges: SnAccountBadge[];
|
||||||
perk_subscription: SnAccountPerkSubscription | null;
|
perkSubscription: SnAccountPerkSubscription | null;
|
||||||
created_at: string;
|
createdAt: string;
|
||||||
updated_at: string;
|
updatedAt: string;
|
||||||
deleted_at: string | null;
|
deletedAt: string | null;
|
||||||
}
|
}
|
||||||
|
57
app/utils/transformKeys.ts
Normal file
57
app/utils/transformKeys.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Transform object keys between snake_case and camelCase
|
||||||
|
*/
|
||||||
|
|
||||||
|
type TransformFunction = (key: string) => string
|
||||||
|
|
||||||
|
const snakeCase: TransformFunction = (key: string): string => {
|
||||||
|
return key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const camelCase: TransformFunction = (key: string): string => {
|
||||||
|
return key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformKeys(obj: unknown, transformFn: TransformFunction): unknown {
|
||||||
|
if (obj === null || obj === undefined) {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(item => transformKeys(item, transformFn))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj === 'object' && obj.constructor === Object) {
|
||||||
|
const transformed: Record<string, unknown> = {}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
const transformedKey = transformFn(key)
|
||||||
|
transformed[transformedKey] = transformKeys(value, transformFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert snake_case keys to camelCase
|
||||||
|
*/
|
||||||
|
export function keysToCamel<T = unknown>(obj: unknown): T {
|
||||||
|
return transformKeys(obj, camelCase) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert camelCase keys to snake_case
|
||||||
|
*/
|
||||||
|
export function keysToSnake<T = unknown>(obj: unknown): T {
|
||||||
|
return transformKeys(obj, snakeCase) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep clone and transform keys
|
||||||
|
*/
|
||||||
|
export function deepTransformKeys<T = unknown>(obj: unknown, transformFn: TransformFunction): T {
|
||||||
|
return JSON.parse(JSON.stringify(transformKeys(obj, transformFn))) as T
|
||||||
|
}
|
Reference in New Issue
Block a user