♻️ Splitting up services (skip ci)

This commit is contained in:
2025-01-10 00:18:26 +08:00
parent df6679bbe3
commit c1140b4e2f
36 changed files with 393 additions and 148 deletions

View File

@@ -1,6 +1,6 @@
import { useUserStore } from '@/services/user'
import { useUserStore } from 'solar-js-sdk'
import { AppBar, AppBarProps, Avatar, IconButton, Toolbar, Typography, useScrollTrigger, useTheme } from '@mui/material'
import { getAttachmentUrl } from '@/services/network'
import { getAttachmentUrl } from 'solar-js-sdk'
import MenuIcon from '@mui/icons-material/Menu'
import AccountCircle from '@mui/icons-material/AccountCircle'
import Link from 'next/link'

View File

@@ -1,5 +1,5 @@
import { SnAttachment } from '@/services/attachment'
import { getAttachmentUrl } from '@/services/network'
import { SnAttachment } from 'solar-js-sdk'
import { getAttachmentUrl } from 'solar-js-sdk'
import { QuestionMark } from '@mui/icons-material'
import { Link, Paper, Typography } from '@mui/material'
import { ComponentProps } from 'react'

View File

@@ -1,14 +1,13 @@
'use client'
import { SnAuthFactor, SnAuthResult, SnAuthTicket } from '@/services/auth'
import { sni } from '@/services/network'
import { setTokenCookies, SnAuthFactor, SnAuthResult, SnAuthTicket } from 'solar-js-sdk'
import { sni } from 'solar-js-sdk'
import { ArrowForward } from '@mui/icons-material'
import { Collapse, Alert, Box, TextField, Button } from '@mui/material'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import ErrorIcon from '@mui/icons-material/Error'
import { setCookie } from 'cookies-next/client'
export interface SnLoginCheckpointForm {
password: string
@@ -44,8 +43,7 @@ export function SnLoginCheckpoint({
})
const atk: string = tokenResp.data['accessToken']
const rtk: string = tokenResp.data['refreshToken']
setCookie('nex_user_atk', atk, { path: '/', maxAge: 2592000 })
setCookie('nex_user_rtk', rtk, { path: '/', maxAge: 2592000 })
setTokenCookies(atk, rtk)
console.log('[Authenticator] User has been logged in. Result atk: ', atk)
}

View File

@@ -1,7 +1,7 @@
'use client'
import { SnAuthFactor, SnAuthTicket } from '@/services/auth'
import { sni } from '@/services/network'
import { SnAuthFactor, SnAuthTicket } from 'solar-js-sdk'
import { sni } from 'solar-js-sdk'
import { Collapse, Alert, Box, Button, Typography, ButtonGroup } from '@mui/material'
import { useState } from 'react'

View File

@@ -1,10 +1,10 @@
'use client'
import { useState } from 'react'
import { sni } from '@/services/network'
import { sni } from 'solar-js-sdk'
import { ArrowForward } from '@mui/icons-material'
import { Alert, Box, Button, Collapse, Link, TextField, Typography } from '@mui/material'
import { SnAuthFactor, SnAuthResult, SnAuthTicket } from '@/services/auth'
import { SnAuthFactor, SnAuthResult, SnAuthTicket } from 'solar-js-sdk'
import { useForm } from 'react-hook-form'
import NextLink from 'next/link'

View File

@@ -1,4 +1,4 @@
import { checkAuthenticatedClient, redirectToLogin } from '@/services/auth'
import { checkAuthenticatedClient, redirectToLogin } from 'solar-js-sdk'
import { JSX, useEffect } from 'react'
import { DashboardLayout, Navigation } from '@toolpad/core'
import { Box, Stack, Typography } from '@mui/material'

View File

@@ -5,7 +5,7 @@ import { Roboto } from 'next/font/google'
import { CapAppBar } from '@/components/CapAppBar'
import { PagesProgressBar as ProgressBar } from 'next-nprogress-bar'
import { AppProvider } from '@toolpad/core/nextjs'
import { useUserStore } from '@/services/user'
import { useUserStore } from 'solar-js-sdk'
import { useEffect } from 'react'
import Head from 'next/head'

View File

@@ -1,6 +1,6 @@
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
import { SnAttachment } from '@/services/attachment'
import { sni } from '@/services/network'
import { SnAttachment } from 'solar-js-sdk'
import { sni } from 'solar-js-sdk'
import { Box, ImageList, ImageListItem, Pagination, useMediaQuery, useTheme } from '@mui/material'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import { useRouter } from 'next/router'

View File

@@ -1,8 +1,8 @@
import { sni } from '@/services/network'
import { sni } from 'solar-js-sdk'
import { Container, Box, Typography, Alert, Collapse, Button, CircularProgress, Card, CardContent } from '@mui/material'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { checkAuthenticatedClient, redirectToLogin, SnAuthTicket } from '@/services/auth'
import { checkAuthenticatedClient, redirectToLogin, SnAuthTicket } from 'solar-js-sdk'
import ErrorIcon from '@mui/icons-material/Error'
import CloseIcon from '@mui/icons-material/Close'

View File

@@ -1,8 +1,8 @@
import { SnLoginCheckpoint } from '@/components/auth/SnLoginCheckpoint'
import { SnLoginRouter } from '@/components/auth/SnLoginRouter'
import { SnLoginStart } from '@/components/auth/SnLoginStart'
import { SnAuthFactor, SnAuthTicket } from '@/services/auth'
import { useUserStore } from '@/services/user'
import { SnAuthFactor, SnAuthTicket } from 'solar-js-sdk'
import { useUserStore } from 'solar-js-sdk'
import { Box, Container, Typography } from '@mui/material'
import { useRouter } from 'next/router'
import { useState } from 'react'

View File

@@ -1,17 +1,7 @@
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
import { MaProduct } from '@/services/matrix/product'
import { sni } from '@/services/network'
import {
Typography,
Container,
Box,
Button,
Grid2 as Grid,
Card,
CardActionArea,
CardContent,
CardActions,
} from '@mui/material'
import { MaProduct } from 'solar-js-sdk'
import { sni } from 'solar-js-sdk'
import { Typography, Container, Box, Button, Grid2 as Grid, Card, CardContent, CardActions } from '@mui/material'
import NextLink from 'next/link'
import { useEffect, useState } from 'react'

View File

@@ -3,8 +3,8 @@ import { Typography, Container, Box, Button, TextField, Collapse, Alert } from '
import { useForm } from 'react-hook-form'
import { useState } from 'react'
import { useRouter } from 'next/router'
import { sni } from 'solar-js-sdk'
import NextLink from 'next/link'
import { sni } from '@/services/network'
import ErrorIcon from '@mui/icons-material/Error'

View File

@@ -1,4 +1,4 @@
import { sni } from '@/services/network'
import { sni } from 'solar-js-sdk'
import { Container, Box, Typography, CircularProgress, Alert, Collapse } from '@mui/material'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'

View File

@@ -1,4 +1,4 @@
import { sni } from '@/services/network'
import { sni } from 'solar-js-sdk'
import { Container, Box, Typography, Alert, Collapse, Button } from '@mui/material'
import { useRouter } from 'next/router'
import { useState } from 'react'

View File

@@ -1,4 +1,4 @@
import { sni } from '@/services/network'
import { sni } from 'solar-js-sdk'
import { Container, Box, Typography, Alert, Collapse, Button, TextField } from '@mui/material'
import { useRouter } from 'next/router'
import { useState } from 'react'

View File

@@ -1,6 +1,6 @@
import { getAttachmentUrl, sni } from '@/services/network'
import { SnPost } from '@/services/post'
import { listAttachment, SnAttachment } from '@/services/attachment'
import { getAttachmentUrl, sni } from 'solar-js-sdk'
import { SnPost } from 'solar-js-sdk'
import { listAttachment, SnAttachment } from 'solar-js-sdk'
import {
Grid2 as Grid,
Alert,

View File

@@ -1,5 +1,5 @@
import { sni } from '@/services/network'
import { SnPost } from '@/services/post'
import { sni } from 'solar-js-sdk'
import { SnPost } from 'solar-js-sdk'
import { GetServerSideProps } from 'next'
import { Feed } from 'feed'

View File

@@ -1,7 +1,7 @@
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
import { SnAttachment, listAttachment } from '@/services/attachment'
import { getAttachmentUrl, sni } from '@/services/network'
import { SnPost } from '@/services/post'
import { SnAttachment, listAttachment } from 'solar-js-sdk'
import { getAttachmentUrl, sni } from 'solar-js-sdk'
import { SnPost } from 'solar-js-sdk'
import { Avatar, Box, Container, Divider, Grid2 as Grid, Link, Pagination, Paper, Typography } from '@mui/material'
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
import NextLink from 'next/link'

View File

@@ -1,5 +1,5 @@
import { sni } from '@/services/network'
import { SnPost } from '@/services/post'
import { sni } from 'solar-js-sdk'
import { SnPost } from 'solar-js-sdk'
import { GetServerSideProps } from 'next'
import { EnumChangefreq, SitemapItem, SitemapStream, streamToPromise } from 'sitemap'
import { Readable } from 'stream'

View File

@@ -1,9 +1,10 @@
import { SnCheckInRecord } from '@/services/checkIn'
import { getAttachmentUrl, sni } from '@/services/network'
import { SnAccount, SnAccountBadgeMapping } from '@/services/user'
import { SnCheckInRecord } from 'solar-js-sdk'
import { getAttachmentUrl, sni } from 'solar-js-sdk'
import { SnAccount } from 'solar-js-sdk'
import { Avatar, Box, Card, CardContent, Container, Grid2 as Grid, Typography } from '@mui/material'
import { LineChart } from '@mui/x-charts'
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
import { SnAccountBadgeMapping } from '@/services/user'
import Image from 'next/image'
export const getServerSideProps = (async (context) => {

View File

@@ -1,14 +1,14 @@
import { checkAuthenticatedClient, redirectToLogin } from '@/services/auth'
import { useUserStore } from '@/services/user'
import { checkAuthenticatedClient, redirectToLogin } from 'solar-js-sdk'
import { useUserStore } from 'solar-js-sdk'
import { Avatar, Box, Button, Container, Typography } from '@mui/material'
import { getAttachmentUrl } from '@/services/network'
import { getAttachmentUrl } from 'solar-js-sdk'
import { useEffect } from 'react'
import { removeTokenCookies } from 'solar-js-sdk'
import Image from 'next/image'
import LogoutIcon from '@mui/icons-material/Logout'
import LaunchIcon from '@mui/icons-material/Launch'
import Link from 'next/link'
import { deleteCookie } from 'cookies-next/client'
export default function UserItself() {
useEffect(() => {
@@ -18,8 +18,7 @@ export default function UserItself() {
const userStore = useUserStore()
function logout() {
deleteCookie('nex_user_atk')
deleteCookie('nex_user_rtk')
removeTokenCookies()
window.location.reload()
}

View File

@@ -1,48 +0,0 @@
import { sni } from './network'
export interface SnAttachment {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
rid: string
uuid: string
size: number
name: string
alt: string
mimetype: string
hash: string
destination: number
refCount: number
contentRating: number
qualityRating: number
cleanedAt?: Date | null
isAnalyzed: boolean
isSelfRef: boolean
isIndexable: boolean
ref?: SnAttachment | null
refId?: number | null
poolId?: number | null
accountId: number
thumbnailId?: number | null
thumbnail?: SnAttachment | null
compressedId?: number | null
compressed?: SnAttachment | null
usermeta: Record<string, any>
metadata: Record<string, any>
}
export async function getAttachment(id: string | number): Promise<SnAttachment> {
const resp = await sni.get<SnAttachment>('/cgi/uc/attachments/' + id + '/meta')
return resp.data
}
export async function listAttachment(id: string[]): Promise<SnAttachment[]> {
const resp = await sni.get<{ data: SnAttachment[] }>('/cgi/uc/attachments', {
params: {
id: id.join(','),
take: id.length,
},
})
return resp.data.data
}

View File

@@ -1,44 +0,0 @@
import { hasCookie } from 'cookies-next/client'
export interface SnAuthResult {
isFinished: boolean
ticket: SnAuthTicket
}
export interface SnAuthTicket {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
stepRemain: number
grantToken?: string | null
accessToken?: string | null
refreshToken?: string | null
ipAddress: string
location: string
userAgent: string
expiredAt?: Date | null
lastGrantAt?: Date | null
availableAt?: Date | null
nonce?: string | null
accountId?: number | null
factorTrail: number[]
}
export interface SnAuthFactor {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
type: number
config?: Record<string, any> | null
accountId?: number | null
}
export function checkAuthenticatedClient(): boolean {
return !!hasCookie('nex_user_atk')
}
export function redirectToLogin() {
window.open('/auth/login?redirect_uri=' + encodeURIComponent(window.location.pathname), '_self')
}

View File

@@ -1,10 +0,0 @@
export interface SnCheckInRecord {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
resultTier: number
resultExperience: number
resultModifiers: number[]
accountId: number
}

View File

@@ -1,25 +0,0 @@
export interface MaProduct {
id: number
created_at: Date
updated_at: Date
deleted_at?: Date
icon: string
name: string
alias: string
description: string
previews: string[]
tags: string[]
meta: MaProductMeta
releases: null
account_id: number
}
export interface MaProductMeta {
id: number
created_at: Date
updated_at: Date
deleted_at?: Date
introduction: string
attachments: string[]
product_id: number
}

View File

@@ -1,77 +0,0 @@
import axios, { AxiosInstance } from 'axios'
import applyCaseMiddleware from 'axios-case-converter'
import { hasCookie, getCookie, setCookie } from 'cookies-next/client'
const baseURL = 'https://api.sn.solsynth.dev'
export const sni: AxiosInstance = (() => {
const inst = axios.create({
baseURL,
})
inst.interceptors.request.use(
async (config) => {
const tk = await refreshToken()
if (tk) config.headers['Authorization'] = `Bearer ${tk}`
return config
},
(error) => error,
)
applyCaseMiddleware(inst, {
ignoreParams: true,
ignoreHeaders: true,
})
return inst
})()
async function refreshToken(): Promise<string | undefined> {
if (!hasCookie('nex_user_atk') || !hasCookie('nex_user_rtk')) return
const ogTk: string = getCookie('nex_user_atk')!
if (!isTokenExpired(ogTk)) return ogTk
const resp = await axios.post(
'/cgi/id/auth/token',
{
refresh_token: getCookie('nex_user_rtk')!,
grant_type: 'refresh_token',
},
{ baseURL },
)
const atk: string = resp.data['access_token']
const rtk: string = resp.data['refresh_token']
setCookie('nex_user_atk', atk, { path: '/', maxAge: 2592000 })
setCookie('nex_user_rtk', rtk, { path: '/', maxAge: 2592000 })
console.log('[Authenticator] Refreshed token...')
return atk
}
function isTokenExpired(token: string): boolean {
try {
const parts = token.split('.')
if (parts.length !== 3) {
throw new Error('Invalid JWT format')
}
const payload = JSON.parse(atob(parts[1]))
if (!payload.exp) {
throw new Error("'exp' claim is missing in the JWT payload")
}
const now = Math.floor(Date.now() / 1000)
return now >= payload.exp
} catch (error) {
console.error('[Authenticator] Something went wrong with token: ', error)
return true
}
}
export function getAttachmentUrl(identifer: string): string {
if (identifer.startsWith('http')) return identifer
return `${baseURL}/cgi/uc/attachments/${identifer}`
}

View File

@@ -1,85 +0,0 @@
export interface SnPost {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
type: string
body: SnPostBody & Record<string, any>
language: string
alias?: string | null
aliasPrefix?: string | null
tags: SnPostTag[]
categories: SnPostCategory[]
replies?: SnPost[] | null
replyId?: number | null
repostId?: number | null
replyTo?: SnPost | null
repostTo?: SnPost | null
visibleUsersList?: number[] | null
invisibleUsersList?: number[] | null
visibility: number
editedAt?: Date | null
pinnedAt?: Date | null
lockedAt?: Date | null
isDraft: boolean
publishedAt?: Date | null
publishedUntil?: Date | null
totalUpvote: number
totalDownvote: number
publisherId: number
publisher: SnPublisher
metric: SnMetric
}
export interface SnPostTag {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date
alias: string
name: string
description: string
posts?: SnPost[]
}
export interface SnPostCategory {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date
alias: string
name: string
description: string
posts?: SnPost[]
}
export interface SnPostBody {
attachments: string[]
content: string
location?: string
thumbnail?: string
title?: string
}
export interface SnMetric {
replyCount: number
reactionCount: number
reactionList: Record<string, number>
}
export interface SnPublisher {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
type: number
name: string
nick: string
description: string
avatar: string
banner: string
totalUpvote: number
totalDownvote: number
realmId?: number | null
accountId: number
}

View File

@@ -1,69 +1,8 @@
import { create } from 'zustand'
import { sni } from './network'
import { hasCookie } from 'cookies-next/client'
import { JSX } from 'react'
import ConstructionIcon from '@mui/icons-material/Construction'
import FlagIcon from '@mui/icons-material/Flag'
export interface SnAccount {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
confirmedAt?: Date | null
contacts?: SnAccountContact[] | null
avatar: string
banner: string
description: string
name: string
nick: string
permNodes: Record<string, any>
profile?: SnAccountProfile | null
badges: SnAccountBadge[]
suspendedAt?: Date | null
affiliatedId?: number | null
affiliatedTo?: number | null
automatedBy?: number | null
automatedId?: number | null
}
export interface SnAccountContact {
accountId: number
content: string
createdAt: Date
deletedAt?: Date | null
id: number
isPrimary: boolean
isPublic: boolean
type: number
updatedAt: Date
verifiedAt?: Date | null
}
export interface SnAccountProfile {
id: number
accountId: number
birthday?: Date | null
createdAt: Date
deletedAt?: Date | null
experience: number
firstName: string
lastName: string
lastSeenAt?: Date | null
updatedAt: Date
}
export interface SnAccountBadge {
id: number
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
type: string
accountId: number
metadata: Record<string, any>
}
export const SnAccountBadgeMapping: Record<string, { icon: JSX.Element; name: string }> = {
'company.staff': {
icon: <ConstructionIcon />,
@@ -74,24 +13,3 @@ export const SnAccountBadgeMapping: Record<string, { icon: JSX.Element; name: st
name: 'Solar Network Natives',
},
}
export interface UserStore {
account: SnAccount | undefined
fetchUser: () => Promise<SnAccount | undefined>
}
export const useUserStore = create<UserStore>((set) => ({
account: undefined,
fetchUser: async (): Promise<SnAccount | undefined> => {
if (!hasCookie('nex_user_atk')) return
try {
const resp = await sni.get<SnAccount>('/cgi/id/users/me')
set({ account: resp.data })
console.log('[Authenticator] Logged in as @' + resp.data.name)
return resp.data
} catch (err) {
console.error('[Authenticator] Unable to get user profile: ', err)
return
}
},
}))