Compare commits
No commits in common. "5d6a018c63e6d832271c71eab0400f0ded5ebd6c" and "945bd5d3578572650461901fd9db4ee744ba9b56" have entirely different histories.
5d6a018c63
...
945bd5d357
@ -9,7 +9,6 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"solar-js-sdk": "./packages/sn",
|
|
||||||
"@emotion/cache": "^11.14.0",
|
"@emotion/cache": "^11.14.0",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
|
175
packages/sn/.gitignore
vendored
175
packages/sn/.gitignore
vendored
@ -1,175 +0,0 @@
|
|||||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
|
|
||||||
logs
|
|
||||||
_.log
|
|
||||||
npm-debug.log_
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# Caches
|
|
||||||
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
|
|
||||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
|
|
||||||
pids
|
|
||||||
_.pid
|
|
||||||
_.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional stylelint cache
|
|
||||||
|
|
||||||
.stylelintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
|
|
||||||
.env
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
|
||||||
|
|
||||||
.temp
|
|
||||||
|
|
||||||
# Docusaurus cache and generated files
|
|
||||||
|
|
||||||
.docusaurus
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v2
|
|
||||||
|
|
||||||
.yarn/cache
|
|
||||||
.yarn/unplugged
|
|
||||||
.yarn/build-state.yml
|
|
||||||
.yarn/install-state.gz
|
|
||||||
.pnp.*
|
|
||||||
|
|
||||||
# IntelliJ based IDEs
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Finder (MacOS) folder config
|
|
||||||
.DS_Store
|
|
Binary file not shown.
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "solar-js-sdk",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"type": "module",
|
|
||||||
"tsup": {
|
|
||||||
"entry": [
|
|
||||||
"src/index.ts"
|
|
||||||
],
|
|
||||||
"splitting": true,
|
|
||||||
"sourcemap": true,
|
|
||||||
"clean": true,
|
|
||||||
"dts": true,
|
|
||||||
"format": "esm"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsup"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bun": "latest",
|
|
||||||
"tsup": "^8.3.5"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^5.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.7.9",
|
|
||||||
"universal-cookie": "^7.2.2",
|
|
||||||
"zustand": "^5.0.3"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export * from './matrix/product'
|
|
||||||
export * from './attachment'
|
|
||||||
export * from './auth'
|
|
||||||
export * from './checkIn'
|
|
||||||
export * from './network'
|
|
||||||
export * from './post'
|
|
||||||
export * from './user'
|
|
@ -1,113 +0,0 @@
|
|||||||
import axios, { type AxiosInstance } from 'axios'
|
|
||||||
import Cookies from 'universal-cookie'
|
|
||||||
import { setTokenCookies } from './auth'
|
|
||||||
|
|
||||||
function toCamelCase(obj: any): any {
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
return obj.map(toCamelCase)
|
|
||||||
} else if (obj && typeof obj === 'object') {
|
|
||||||
return Object.keys(obj).reduce((result: any, key) => {
|
|
||||||
const camelKey = key.replace(/_([a-z])/g, (_, char) => char.toUpperCase())
|
|
||||||
result[camelKey] = toCamelCase(obj[key])
|
|
||||||
return result
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// function toSnakeCase(obj: any): any {
|
|
||||||
// if (Array.isArray(obj)) {
|
|
||||||
// return obj.map(toSnakeCase)
|
|
||||||
// } else if (obj && typeof obj === 'object') {
|
|
||||||
// return Object.keys(obj).reduce((result: any, key) => {
|
|
||||||
// const snakeKey = key.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`)
|
|
||||||
// result[snakeKey] = toSnakeCase(obj[key])
|
|
||||||
// return result
|
|
||||||
// }, {})
|
|
||||||
// }
|
|
||||||
// return obj
|
|
||||||
// }
|
|
||||||
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
inst.interceptors.response.use(
|
|
||||||
(response) => {
|
|
||||||
if (response.data) {
|
|
||||||
response.data = toCamelCase(response.data)
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
if (error.response && error.response.data) {
|
|
||||||
error.response.data = toCamelCase(error.response.data)
|
|
||||||
}
|
|
||||||
return Promise.reject(error)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return inst
|
|
||||||
})()
|
|
||||||
|
|
||||||
async function refreshToken(): Promise<string | undefined> {
|
|
||||||
const cookies = new Cookies()
|
|
||||||
if (!cookies.get('nex_user_atk') || !cookies.get('nex_user_rtk')) return
|
|
||||||
|
|
||||||
const ogTk: string = cookies.get('nex_user_atk')!
|
|
||||||
if (!isTokenExpired(ogTk)) return ogTk
|
|
||||||
|
|
||||||
const resp = await axios.post(
|
|
||||||
'/cgi/id/auth/token',
|
|
||||||
{
|
|
||||||
refresh_token: cookies.get('nex_user_rtk')!,
|
|
||||||
grant_type: 'refresh_token',
|
|
||||||
},
|
|
||||||
{ baseURL },
|
|
||||||
)
|
|
||||||
const atk: string = resp.data['access_token']
|
|
||||||
const rtk: string = resp.data['refresh_token']
|
|
||||||
setTokenCookies(atk, rtk)
|
|
||||||
|
|
||||||
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}`
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
import { create } from 'zustand'
|
|
||||||
import { sni } from './network'
|
|
||||||
import Cookies from 'universal-cookie'
|
|
||||||
|
|
||||||
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 interface UserStore {
|
|
||||||
account: SnAccount | undefined
|
|
||||||
fetchUser: () => Promise<SnAccount | undefined>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useUserStore = create<UserStore>((set) => ({
|
|
||||||
account: undefined,
|
|
||||||
fetchUser: async (): Promise<SnAccount | undefined> => {
|
|
||||||
const cookies = new Cookies()
|
|
||||||
if (!cookies.get('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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}))
|
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
// Enable latest features
|
|
||||||
"lib": ["ESNext", "DOM"],
|
|
||||||
"target": "ESNext",
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"allowJs": true,
|
|
||||||
|
|
||||||
// Bundler mode
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"verbatimModuleSyntax": true,
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
// Best practices
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
|
|
||||||
// Some stricter flags (disabled by default)
|
|
||||||
"noUnusedLocals": false,
|
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noPropertyAccessFromIndexSignature": false
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import { useUserStore } from 'solar-js-sdk'
|
import { useUserStore } from '@/services/user'
|
||||||
import { AppBar, AppBarProps, Avatar, IconButton, Toolbar, Typography, useScrollTrigger, useTheme } from '@mui/material'
|
import { AppBar, AppBarProps, Avatar, IconButton, Toolbar, Typography, useScrollTrigger, useTheme } from '@mui/material'
|
||||||
import { getAttachmentUrl } from 'solar-js-sdk'
|
import { getAttachmentUrl } from '@/services/network'
|
||||||
import MenuIcon from '@mui/icons-material/Menu'
|
import MenuIcon from '@mui/icons-material/Menu'
|
||||||
import AccountCircle from '@mui/icons-material/AccountCircle'
|
import AccountCircle from '@mui/icons-material/AccountCircle'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { SnAttachment } from 'solar-js-sdk'
|
import { SnAttachment } from '@/services/attachment'
|
||||||
import { getAttachmentUrl } from 'solar-js-sdk'
|
import { getAttachmentUrl } from '@/services/network'
|
||||||
import { QuestionMark } from '@mui/icons-material'
|
import { QuestionMark } from '@mui/icons-material'
|
||||||
import { Link, Paper, Typography } from '@mui/material'
|
import { Link, Paper, Typography } from '@mui/material'
|
||||||
import { ComponentProps } from 'react'
|
import { ComponentProps } from 'react'
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { setTokenCookies, SnAuthFactor, SnAuthResult, SnAuthTicket } from 'solar-js-sdk'
|
import { SnAuthFactor, SnAuthResult, SnAuthTicket } from '@/services/auth'
|
||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { ArrowForward } from '@mui/icons-material'
|
import { ArrowForward } from '@mui/icons-material'
|
||||||
import { Collapse, Alert, Box, TextField, Button } from '@mui/material'
|
import { Collapse, Alert, Box, TextField, Button } from '@mui/material'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
|
|
||||||
import ErrorIcon from '@mui/icons-material/Error'
|
import ErrorIcon from '@mui/icons-material/Error'
|
||||||
|
import { setCookie } from 'cookies-next/client'
|
||||||
|
|
||||||
export interface SnLoginCheckpointForm {
|
export interface SnLoginCheckpointForm {
|
||||||
password: string
|
password: string
|
||||||
@ -43,7 +44,8 @@ export function SnLoginCheckpoint({
|
|||||||
})
|
})
|
||||||
const atk: string = tokenResp.data['accessToken']
|
const atk: string = tokenResp.data['accessToken']
|
||||||
const rtk: string = tokenResp.data['refreshToken']
|
const rtk: string = tokenResp.data['refreshToken']
|
||||||
setTokenCookies(atk, rtk)
|
setCookie('nex_user_atk', atk, { path: '/', maxAge: 2592000 })
|
||||||
|
setCookie('nex_user_rtk', rtk, { path: '/', maxAge: 2592000 })
|
||||||
console.log('[Authenticator] User has been logged in. Result atk: ', atk)
|
console.log('[Authenticator] User has been logged in. Result atk: ', atk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { SnAuthFactor, SnAuthTicket } from 'solar-js-sdk'
|
import { SnAuthFactor, SnAuthTicket } from '@/services/auth'
|
||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Collapse, Alert, Box, Button, Typography, ButtonGroup } from '@mui/material'
|
import { Collapse, Alert, Box, Button, Typography, ButtonGroup } from '@mui/material'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { ArrowForward } from '@mui/icons-material'
|
import { ArrowForward } from '@mui/icons-material'
|
||||||
import { Alert, Box, Button, Collapse, Link, TextField, Typography } from '@mui/material'
|
import { Alert, Box, Button, Collapse, Link, TextField, Typography } from '@mui/material'
|
||||||
import { SnAuthFactor, SnAuthResult, SnAuthTicket } from 'solar-js-sdk'
|
import { SnAuthFactor, SnAuthResult, SnAuthTicket } from '@/services/auth'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { checkAuthenticatedClient, redirectToLogin } from 'solar-js-sdk'
|
import { checkAuthenticatedClient, redirectToLogin } from '@/services/auth'
|
||||||
import { JSX, useEffect } from 'react'
|
import { JSX, useEffect } from 'react'
|
||||||
import { DashboardLayout, Navigation } from '@toolpad/core'
|
import { DashboardLayout, Navigation } from '@toolpad/core'
|
||||||
import { Box, Stack, Typography } from '@mui/material'
|
import { Box, Stack, Typography } from '@mui/material'
|
||||||
|
@ -5,7 +5,7 @@ import { Roboto } from 'next/font/google'
|
|||||||
import { CapAppBar } from '@/components/CapAppBar'
|
import { CapAppBar } from '@/components/CapAppBar'
|
||||||
import { PagesProgressBar as ProgressBar } from 'next-nprogress-bar'
|
import { PagesProgressBar as ProgressBar } from 'next-nprogress-bar'
|
||||||
import { AppProvider } from '@toolpad/core/nextjs'
|
import { AppProvider } from '@toolpad/core/nextjs'
|
||||||
import { useUserStore } from 'solar-js-sdk'
|
import { useUserStore } from '@/services/user'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
|
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
|
||||||
import { SnAttachment } from 'solar-js-sdk'
|
import { SnAttachment } from '@/services/attachment'
|
||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Box, ImageList, ImageListItem, Pagination, useMediaQuery, useTheme } from '@mui/material'
|
import { Box, ImageList, ImageListItem, Pagination, useMediaQuery, useTheme } from '@mui/material'
|
||||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Container, Box, Typography, Alert, Collapse, Button, CircularProgress, Card, CardContent } from '@mui/material'
|
import { Container, Box, Typography, Alert, Collapse, Button, CircularProgress, Card, CardContent } from '@mui/material'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { checkAuthenticatedClient, redirectToLogin, SnAuthTicket } from 'solar-js-sdk'
|
import { checkAuthenticatedClient, redirectToLogin, SnAuthTicket } from '@/services/auth'
|
||||||
|
|
||||||
import ErrorIcon from '@mui/icons-material/Error'
|
import ErrorIcon from '@mui/icons-material/Error'
|
||||||
import CloseIcon from '@mui/icons-material/Close'
|
import CloseIcon from '@mui/icons-material/Close'
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { SnLoginCheckpoint } from '@/components/auth/SnLoginCheckpoint'
|
import { SnLoginCheckpoint } from '@/components/auth/SnLoginCheckpoint'
|
||||||
import { SnLoginRouter } from '@/components/auth/SnLoginRouter'
|
import { SnLoginRouter } from '@/components/auth/SnLoginRouter'
|
||||||
import { SnLoginStart } from '@/components/auth/SnLoginStart'
|
import { SnLoginStart } from '@/components/auth/SnLoginStart'
|
||||||
import { SnAuthFactor, SnAuthTicket } from 'solar-js-sdk'
|
import { SnAuthFactor, SnAuthTicket } from '@/services/auth'
|
||||||
import { useUserStore } from 'solar-js-sdk'
|
import { useUserStore } from '@/services/user'
|
||||||
import { Box, Container, Typography } from '@mui/material'
|
import { Box, Container, Typography } from '@mui/material'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
@ -26,7 +26,7 @@ export default function ConsoleLanding() {
|
|||||||
<Typography variant="subtitle1">of the Solar Network</Typography>
|
<Typography variant="subtitle1">of the Solar Network</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Grid container columns={{ xs: 1, sm: 2, md: 3 }} spacing={4}>
|
<Grid container columns={{ xs: 2, sm: 2, md: 3, lg: 4 }} spacing={4}>
|
||||||
<Grid size={1}>
|
<Grid size={1}>
|
||||||
<NextLink passHref href="/console/matrix">
|
<NextLink passHref href="/console/matrix">
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
|
import { ConsoleLayout, getConsoleStaticProps } from '@/components/layouts/ConsoleLayout'
|
||||||
import { MaProduct } from 'solar-js-sdk'
|
import { MaProduct } from '@/services/matrix/product'
|
||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Typography, Container, Box, Button, Grid2 as Grid, Card, CardContent, CardActions } from '@mui/material'
|
import { Typography, Container, Box, Button, Grid2 as Grid, Card, CardActionArea, CardContent } from '@mui/material'
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
@ -36,21 +36,21 @@ export default function MatrixMarketplace() {
|
|||||||
Matrix Marketplace
|
Matrix Marketplace
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container columns={{ xs: 1, sm: 2, md: 3 }} spacing={4}>
|
<Grid container columns={{ xs: 2, sm: 2, md: 3, lg: 4 }} spacing={4}>
|
||||||
{products.map((p) => (
|
{products.map((p) => (
|
||||||
<Grid size={1} key={p.id}>
|
<Grid size={1} key={p.id}>
|
||||||
<Card sx={{ width: '100%' }}>
|
<NextLink passHref href="/console/matrix">
|
||||||
<CardContent>
|
<CardActionArea>
|
||||||
<Typography variant="h5" gutterBottom>
|
<Card sx={{ width: '100%' }}>
|
||||||
{p.name}
|
<CardContent>
|
||||||
</Typography>
|
<Typography variant="h5" gutterBottom>
|
||||||
<Typography variant="body1">{p.description}</Typography>
|
{p.name}
|
||||||
</CardContent>
|
</Typography>
|
||||||
<CardActions>
|
<Typography variant="body1">{p.description}</Typography>
|
||||||
<Button size="small">Details</Button>
|
</CardContent>
|
||||||
<Button size="small">Edit</Button>
|
</Card>
|
||||||
</CardActions>
|
</CardActionArea>
|
||||||
</Card>
|
</NextLink>
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -3,8 +3,8 @@ import { Typography, Container, Box, Button, TextField, Collapse, Alert } from '
|
|||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { sni } from 'solar-js-sdk'
|
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
|
import { sni } from '@/services/network'
|
||||||
|
|
||||||
import ErrorIcon from '@mui/icons-material/Error'
|
import ErrorIcon from '@mui/icons-material/Error'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Container, Box, Typography, CircularProgress, Alert, Collapse } from '@mui/material'
|
import { Container, Box, Typography, CircularProgress, Alert, Collapse } from '@mui/material'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Container, Box, Typography, Alert, Collapse, Button } from '@mui/material'
|
import { Container, Box, Typography, Alert, Collapse, Button } from '@mui/material'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { Container, Box, Typography, Alert, Collapse, Button, TextField } from '@mui/material'
|
import { Container, Box, Typography, Alert, Collapse, Button, TextField } from '@mui/material'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getAttachmentUrl, sni } from 'solar-js-sdk'
|
import { getAttachmentUrl, sni } from '@/services/network'
|
||||||
import { SnPost } from 'solar-js-sdk'
|
import { SnPost } from '@/services/post'
|
||||||
import { listAttachment, SnAttachment } from 'solar-js-sdk'
|
import { listAttachment, SnAttachment } from '@/services/attachment'
|
||||||
import {
|
import {
|
||||||
Grid2 as Grid,
|
Grid2 as Grid,
|
||||||
Alert,
|
Alert,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { SnPost } from 'solar-js-sdk'
|
import { SnPost } from '@/services/post'
|
||||||
import { GetServerSideProps } from 'next'
|
import { GetServerSideProps } from 'next'
|
||||||
import { Feed } from 'feed'
|
import { Feed } from 'feed'
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
|
import { AttachmentItem } from '@/components/attachments/AttachmentItem'
|
||||||
import { SnAttachment, listAttachment } from 'solar-js-sdk'
|
import { SnAttachment, listAttachment } from '@/services/attachment'
|
||||||
import { getAttachmentUrl, sni } from 'solar-js-sdk'
|
import { getAttachmentUrl, sni } from '@/services/network'
|
||||||
import { SnPost } from 'solar-js-sdk'
|
import { SnPost } from '@/services/post'
|
||||||
import { Avatar, Box, Container, Divider, Grid2 as Grid, Link, Pagination, Paper, Typography } from '@mui/material'
|
import { Avatar, Box, Container, Divider, Grid2 as Grid, Link, Pagination, Paper, Typography } from '@mui/material'
|
||||||
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
import { GetServerSideProps, InferGetServerSidePropsType } from 'next'
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { sni } from 'solar-js-sdk'
|
import { sni } from '@/services/network'
|
||||||
import { SnPost } from 'solar-js-sdk'
|
import { SnPost } from '@/services/post'
|
||||||
import { GetServerSideProps } from 'next'
|
import { GetServerSideProps } from 'next'
|
||||||
import { EnumChangefreq, SitemapItem, SitemapStream, streamToPromise } from 'sitemap'
|
import { EnumChangefreq, SitemapItem, SitemapStream, streamToPromise } from 'sitemap'
|
||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { SnCheckInRecord } from 'solar-js-sdk'
|
import { SnCheckInRecord } from '@/services/checkIn'
|
||||||
import { getAttachmentUrl, sni } from 'solar-js-sdk'
|
import { getAttachmentUrl, sni } from '@/services/network'
|
||||||
import { SnAccount } from 'solar-js-sdk'
|
import { SnAccount, SnAccountBadgeMapping } from '@/services/user'
|
||||||
import { Avatar, Box, Card, CardContent, Container, Grid2 as Grid, Typography } from '@mui/material'
|
import { Avatar, Box, Card, CardContent, Container, Grid2 as Grid, Typography } from '@mui/material'
|
||||||
import { LineChart } from '@mui/x-charts'
|
import { LineChart } from '@mui/x-charts'
|
||||||
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
|
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'
|
||||||
import { SnAccountBadgeMapping } from '@/services/user'
|
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
export const getServerSideProps = (async (context) => {
|
export const getServerSideProps = (async (context) => {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { checkAuthenticatedClient, redirectToLogin } from 'solar-js-sdk'
|
import { checkAuthenticatedClient, redirectToLogin } from '@/services/auth'
|
||||||
import { useUserStore } from 'solar-js-sdk'
|
import { useUserStore } from '@/services/user'
|
||||||
import { Avatar, Box, Button, Container, Typography } from '@mui/material'
|
import { Avatar, Box, Button, Container, Typography } from '@mui/material'
|
||||||
import { getAttachmentUrl } from 'solar-js-sdk'
|
import { getAttachmentUrl } from '@/services/network'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { removeTokenCookies } from 'solar-js-sdk'
|
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
|
||||||
import LogoutIcon from '@mui/icons-material/Logout'
|
import LogoutIcon from '@mui/icons-material/Logout'
|
||||||
import LaunchIcon from '@mui/icons-material/Launch'
|
import LaunchIcon from '@mui/icons-material/Launch'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import { deleteCookie } from 'cookies-next/client'
|
||||||
|
|
||||||
export default function UserItself() {
|
export default function UserItself() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -18,7 +18,8 @@ export default function UserItself() {
|
|||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
removeTokenCookies()
|
deleteCookie('nex_user_atk')
|
||||||
|
deleteCookie('nex_user_rtk')
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Cookies from 'universal-cookie'
|
import { hasCookie } from 'cookies-next/client'
|
||||||
|
|
||||||
export interface SnAuthResult {
|
export interface SnAuthResult {
|
||||||
isFinished: boolean
|
isFinished: boolean
|
||||||
@ -35,21 +35,8 @@ export interface SnAuthFactor {
|
|||||||
accountId?: number | null
|
accountId?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setTokenCookies(atk: string, rtk: string) {
|
|
||||||
const cookies = new Cookies()
|
|
||||||
cookies.set('nex_user_atk', atk, { path: '/', maxAge: 2592000 })
|
|
||||||
cookies.set('nex_user_rtk', rtk, { path: '/', maxAge: 2592000 })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeTokenCookies() {
|
|
||||||
const cookies = new Cookies()
|
|
||||||
cookies.remove('nex_user_atk')
|
|
||||||
cookies.remove('nex_user_rtk')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkAuthenticatedClient(): boolean {
|
export function checkAuthenticatedClient(): boolean {
|
||||||
const cookies = new Cookies()
|
return !!hasCookie('nex_user_atk')
|
||||||
return !!cookies.get('nex_user_atk')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function redirectToLogin() {
|
export function redirectToLogin() {
|
77
src/services/network.ts
Normal file
77
src/services/network.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
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}`
|
||||||
|
}
|
@ -1,8 +1,69 @@
|
|||||||
|
import { create } from 'zustand'
|
||||||
|
import { sni } from './network'
|
||||||
|
import { hasCookie } from 'cookies-next/client'
|
||||||
import { JSX } from 'react'
|
import { JSX } from 'react'
|
||||||
|
|
||||||
import ConstructionIcon from '@mui/icons-material/Construction'
|
import ConstructionIcon from '@mui/icons-material/Construction'
|
||||||
import FlagIcon from '@mui/icons-material/Flag'
|
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 }> = {
|
export const SnAccountBadgeMapping: Record<string, { icon: JSX.Element; name: string }> = {
|
||||||
'company.staff': {
|
'company.staff': {
|
||||||
icon: <ConstructionIcon />,
|
icon: <ConstructionIcon />,
|
||||||
@ -13,3 +74,24 @@ export const SnAccountBadgeMapping: Record<string, { icon: JSX.Element; name: st
|
|||||||
name: 'Solar Network Natives',
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user