Compare commits

..

No commits in common. "master" and "refactor/nuxtjs" have entirely different histories.

18 changed files with 87 additions and 158 deletions

View File

@ -22,8 +22,8 @@ const { t } = useI18n()
const projects: { [id: string]: [string, string] } = {
"solar-network": ["Solar Network", "https://solsynth.dev/products/solar-network"],
"capital": ["Capital", "https://git.solsynth.dev/Goatworks/Capital"],
"passport": ["HyperNet.Passport", "https://git.solsynth.dev/HyperNet/Passport"],
"paperclip": ["HyperNet.Paperclip", "https://git.solsynth.dev/HyperNet/Paperclip"],
"passport": ["Hydrogen.Passport", "https://git.solsynth.dev/Hydrogen/Passport"],
"paperclip": ["Hydrogen.Paperclip", "https://git.solsynth.dev/Hydrogen/Paperclip"],
"roadsign": ["RoadSign", "https://git.solsynth.dev/Goatworks/RoadSign"],
}
</script>

View File

@ -35,9 +35,8 @@ As used herein, account number, account, and Solarpass refer to the User's accou
- Partial deactivation: Partial disabling of the user's rights, e.g. uploading of files, publishing of posts, etc.
- Disablement: The user's entire account and all rights of Solsynth LLC to use other services are disabled. We also reserve the right to delete the relevant data.
5. A natural person can register and own only one Solarpass account, and we reserve the right to take action against other sub-accounts of the same User for deletion of data.
6. The transfer and sale of Solarpass accounts are strictly prohibited. If such behavior is discovered, measures will be taken to delete the relevant data immediately.
7. If a user opens a sub-account in any way during the penalty period in an attempt to evade the penalty, the sub-account shall be subject to deletion of data and the penalty shall be escalated or the time limit extended, as the case may be.
8. Bot accounts opened through the Developer Portal are not considered sub-accounts. *For more information on the use of bot accounts, please refer to the Developer Rules (/terms/developer-rules).
6. If a user opens a sub-account in any way during the penalty period in an attempt to evade the penalty, the sub-account shall be subject to deletion of data and the penalty shall be escalated or the time limit extended, as the case may be.
7. Bot accounts opened through the Developer Portal are not considered sub-accounts. *For more information on the use of bot accounts, please refer to the Developer Rules (/terms/developer-rules).
## 4. User Generated Content

View File

@ -35,9 +35,8 @@ date: 2025-03-19T16:12:21.897Z
- 部份停权:禁用用户的部份权利,例如上传文件、发布帖子等。
- 禁用:禁用用户的整个帐号和所有 Solsynth LLC 使用其他服务的权利。同时我们保留删除相关数据的权利。
5. 一个自然人只能注册、拥有一个 Solarpass 帐号,我们有权对其他同用户的子帐号采取删除数据的措施。
6. 关于 Solarpass 帐号的转让、出售是绝对禁止的行为,关于发现相关行为将立即采取删除相关数据的措施。
7. 若用户在处罚期间采取任何方式开设子帐号试图逃避处罚,应当对子帐号采取删除数据的措施,并且视情况升级处罚或延长时限。
8. 通过「开发者门户」开设的机器人帐号不属于子帐号范畴。*关于「机器人帐号」的使用规定,详见 [开发者守则](/terms/developer-rules)*
6. 若用户在处罚期间采取任何方式开设子帐号试图逃避处罚,应当对子帐号采取删除数据的措施,并且视情况升级处罚或延长时限。
7. 通过「开发者门户」开设的机器人帐号不属于子帐号范畴。*关于「机器人帐号」的使用规定,详见 [开发者守则](/terms/developer-rules)*
## 4. 用户生成内容

View File

@ -1,35 +1,24 @@
<template>
<v-app-bar app flat color="surface" class="app-bar-blur">
<v-container fluid class="mx-auto d-flex align-center justify-center pr-8 relative">
<v-app-bar-nav-icon @click="openDrawer = !openDrawer" class="z-10" />
<v-container fluid class="mx-auto d-flex align-center justify-center pr-8">
<v-app-bar-nav-icon @click="openDrawer = !openDrawer" />
<nuxt-link to="/" exact class="z-10">
<h2 v-if="isLargeScreen">Solsynth LLC</h2>
<v-icon v-else icon="mdi-home" />
<nuxt-link to="/" exact>
<h2>Solsynth LLC</h2>
</nuxt-link>
<v-spacer></v-spacer>
<div class="absolute left-0 right-0 flex justify-center gap-2 w-screen">
<v-btn v-if="isLargeScreen" v-for="item in navItems" :to="item.to" exact :prepend-icon="item.icon">{{
t(item.title)
}}</v-btn>
<v-menu location="bottom center" v-else>
<template v-slot:activator="{ props }">
<v-btn v-bind="props" icon="mdi-dots-horizontal-circle" slim size="small" />
</template>
<v-list nav slim class="w-[280px]">
<v-list-item v-for="item in navItems" :to="item.to" :prepend-icon="item.icon">
<v-list-item-title>{{ t(item.title) }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<div class="flex gap-2">
<v-btn to="/products" exact prepend-icon="mdi-shape">{{ t("navProducts") }}</v-btn>
<v-btn to="/posts" exact prepend-icon="mdi-note-text">{{ t("navPosts") }}</v-btn>
<v-btn to="/gallery" exact prepend-icon="mdi-image-multiple">{{ t("navGallery") }}</v-btn>
</div>
<v-spacer></v-spacer>
<locale-select class="z-10" />
<user-menu class="z-10" />
<locale-select />
<user-menu />
</v-container>
</v-app-bar>
@ -58,38 +47,9 @@
</template>
<script setup lang="ts">
import { useBreakpoints, breakpointsVuetifyV3 } from "@vueuse/core"
const { t } = useI18n()
const openDrawer = ref(false)
const breakpoints = useBreakpoints(breakpointsVuetifyV3)
const isLargeScreen = computed(() => breakpoints.isGreaterOrEqual("md").valueOf())
interface NavItem {
icon: string
title: string
to: string
}
const navItems: NavItem[] = [
{
icon: "mdi-shape",
title: "navProducts",
to: "/products",
},
{
icon: "mdi-note-text",
title: "navPosts",
to: "/posts",
},
{
icon: "mdi-image-multiple",
title: "navGallery",
to: "/gallery",
},
]
</script>
<style lang="css" scoped>

View File

@ -1,6 +0,0 @@
export default defineNuxtRouteMiddleware((to) => {
// No further supported path prefix localization
if (to.path.startsWith("/zh-CN")) {
return navigateTo(to.fullPath.replace("/zh-CN", ""))
}
})

View File

@ -53,8 +53,8 @@ export default defineNuxtConfig({
},
routeRules: {
"/.well-known/**": {
proxy: "/api/well-known/**",
"/.well-known/openid-configuration": {
proxy: "/api/well-known/openid-configuration",
},
},
@ -143,12 +143,6 @@ export default defineNuxtConfig({
transpile: ["vuetify"],
},
umami: {
id: "eef151fb-07e2-461b-8b7f-2547aab735d4",
host: "https://us.umami.is",
autoTrack: true,
},
modules: [
"@unocss/nuxt",
"@nuxt/content",
@ -158,7 +152,6 @@ export default defineNuxtConfig({
"@nuxtjs/i18n",
"nuxt-schema-org",
"@vueuse/motion/nuxt",
"nuxt-umami",
(_options, nuxt) => {
nuxt.hooks.hook("vite:extendConfig", (config) => {
// @ts-expect-error
@ -167,6 +160,10 @@ export default defineNuxtConfig({
},
],
gtag: {
id: "G-ZFJ7RX0JXF",
},
vite: {
vue: {
template: {

View File

@ -18,13 +18,11 @@
"@nuxtjs/sitemap": "^6.1.5",
"@octokit/rest": "^21.1.1",
"@pinia/nuxt": "^0.5.5",
"@vueuse/core": "^13.0.0",
"@vueuse/motion": "^3.0.3",
"feed": "^4.2.2",
"nuxt": "^3.16.0",
"nuxt-gtag": "^2.1.0",
"nuxt-schema-org": "^3.5.0",
"nuxt-umami": "3.2.0",
"pinia": "^2.3.1",
"rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.1",

View File

@ -46,8 +46,8 @@
<span>Solar Network Attachment Web Preview</span>
<span
>Powered by
<a class="underline" target="_blank" href="https://git.solsynth.dev/HyperNet/Paperclip"
>HyperNet.Paperclip</a
<a class="underline" target="_blank" href="https://git.solsynth.dev/Hydrogen/Paperclip"
>Hydrogen.Paperclip</a
></span
>
</div>

View File

@ -12,7 +12,7 @@
enter: {
y: 0,
opacity: 1,
transition: { duration: 0.8 },
transition: { duration: 0.8 }
},
}"
:src="Logo"
@ -107,13 +107,11 @@ const { data: products } = await useAsyncData("products", () => {
const canvasRef = ref(null)
onMounted(() => {
const isDarkMode = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
const canvas: HTMLCanvasElement = canvasRef.value!
const ctx = canvas.getContext("2d")!
const dpr = window.devicePixelRatio || 1
canvas.width = window.innerWidth * dpr
canvas.height = window.innerHeight * dpr
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
let particles: Particle[] = []
const numParticles = 100
@ -141,10 +139,10 @@ onMounted(() => {
}
draw() {
ctx.beginPath()
ctx.arc(this.x * dpr, this.y * dpr, this.size * dpr, 0, Math.PI * 2)
ctx.fillStyle = isDarkMode ? "rgba(255, 255, 255, 0.8)" : "rgba(0, 0, 0, 0.8)"
ctx.fill()
ctx.beginPath();
ctx.arc(this.x * dpr, this.y * dpr, this.size * dpr, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fill();
}
}
@ -158,17 +156,17 @@ onMounted(() => {
function drawLines() {
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
let dx = particles[i].x - particles[j].x
let dy = particles[i].y - particles[j].y
let distance = Math.sqrt(dx * dx + dy * dy)
let dx = particles[i].x - particles[j].x;
let dy = particles[i].y - particles[j].y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
ctx.beginPath()
ctx.moveTo(particles[i].x * dpr, particles[i].y * dpr)
ctx.lineTo(particles[j].x * dpr, particles[j].y * dpr)
ctx.strokeStyle = isDarkMode ? "rgba(255, 255, 255, 0.2)" : "rgba(0, 0, 0, 0.2)"
ctx.lineWidth = 0.5 * dpr
ctx.stroke()
ctx.beginPath();
ctx.moveTo(particles[i].x * dpr, particles[i].y * dpr);
ctx.lineTo(particles[j].x * dpr, particles[j].y * dpr);
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.lineWidth = 0.5 * dpr;
ctx.stroke();
}
}
}

View File

@ -6,34 +6,25 @@
<div class="my-5 mx-4 flex flex-row gap-4">
<v-avatar :image="urlOfAvatar" />
<div class="flex flex-col">
<span
>{{ account?.nick }} <span class="text-xs">@{{ account?.name }}</span></span
>
<p>
{{ accountStatus.status ? accountStatus.status.label : accountStatus["is_online"] ? "Online" : "Offline" }}
</p>
<span>{{ account?.nick }} <span class="text-xs">@{{ account?.name }}</span></span>
<span class="text-sm">{{ account?.description }}</span>
</div>
</div>
<div class="mb-7">
</div>
<v-row>
<v-col cols="12" lg="8">
<v-card>
<v-card-text v-if="accountPageStatus.valueOf() === 'success'">
<div class="prose prose-sm" style="max-width: unset">
<m-d-c :value="accountPage.content" />
</div>
</v-card-text>
<v-card-text v-else>
<p class="font-italic">The user has no account page.</p>
</v-card-text>
</v-card>
<v-col row="12" lg="8">
<post-list class="mx-[-2.5ch] mt-[-16px]" v-if="account" :author="account.name" />
</v-col>
<v-col cols="12" lg="4" order="first" order-lg="last">
<v-col row="12" lg="4" order="first" order-lg="last">
<div class="sticky top-0 h-fit">
<v-card prepend-icon="mdi-identifier" title="About">
<v-card-text>
<p><b>Description</b></p>
<p>{{ account?.profile.description }}</p>
<p>{{ account.description }}</p>
<p class="mt-3"><b>Joined At</b></p>
<p>{{ new Date(account.created_at).toLocaleString() }}</p>
</v-card-text>
@ -50,6 +41,8 @@ const { t } = useI18n()
const route = useRoute()
const config = useRuntimeConfig()
const tab = ref(1)
const { data: account } = await useFetch<any>(`${config.public.solarNetworkApi}/cgi/id/users/${route.params.name}`)
if (account.value == null) {
@ -59,17 +52,6 @@ if (account.value == null) {
})
}
const { data: accountPage, status: accountPageStatus } = await useFetch<any>(
`${config.public.solarNetworkApi}/cgi/id/users/${route.params.name}/page`,
)
const { data: accountStatus, status: accountStatusStatus } = await useFetch<any>(
`${config.public.solarNetworkApi}/cgi/id/users/${route.params.name}/status`,
)
const urlOfAvatar = computed(() =>
account.value?.avatar ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${account.value.avatar}` : void 0,
)
const urlOfBanner = computed(() =>
account.value?.banner ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${account.value.banner}` : void 0,
)
const urlOfAvatar = computed(() => account.value?.avatar ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${account.value.avatar}` : void 0)
const urlOfBanner = computed(() => account.value?.banner ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${account.value.banner}` : void 0)
</script>

View File

@ -7,7 +7,7 @@
<v-avatar :image="urlOfAvatar" />
<div class="flex flex-col">
<span>{{ auth.userinfo?.nick }} <span class="text-xs">@{{ auth.userinfo?.name }}</span></span>
<span class="text-sm">{{ auth.userinfo?.profile?.description }}</span>
<span class="text-sm">{{ auth.userinfo?.description }}</span>
</div>
</div>
@ -39,7 +39,7 @@
</v-card-text>
</v-card>
<v-card class="w-28 aspect-square" href="https://kb.solsynth.dev" target="_blank">
<v-card class="w-28 aspect-square" to="/docs">
<v-card-text class="flex flex-col justify-center items-center text-center h-full">
<v-icon icon="mdi-library" size="32" />
<span class="text-sm mt-1.75">Knowledge Base</span>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -5,7 +5,7 @@ export default defineSitemapEventHandler(async () => {
const result = await res.json()
return result.data.map((item: any) => asSitemapUrl({
loc: item.alias ? `/posts/${item.alias_prefix}/${item.alias}` : `/posts/${item.id}`,
loc: item.alias ? `/posts/${item.area_alias}/${item.alias}` : `/posts/${item.id}`,
lastmod: item.edited_at ?? item.published_at,
priority: 0.7,
_sitemap: "posts",

View File

@ -1,9 +0,0 @@
import { defineEventHandler } from 'h3'
export default defineEventHandler(async () => {
const config = useRuntimeConfig();
const resp = await fetch(`${config.public.solarNetworkApi}/cgi/id/well-known/jwks`)
return await resp.json()
})

View File

@ -1,20 +1,31 @@
import { defineEventHandler } from 'h3'
export default defineEventHandler((event) => {
const config = useRuntimeConfig()
export default defineEventHandler(async () => {
const config = useRuntimeConfig();
const siteUrl = config.public.siteUrl
const resp = await fetch(`${config.public.solarNetworkApi}/cgi/id/well-known/openid-configuration`)
const out: Record<string, any> = await resp.json()
out['authorization_endpoint'] = `${siteUrl}/auth/authorize`
out['jwks_uri'] = `${siteUrl}/.well-known/jwks`
for (const [k, v] of Object.entries(out)) {
if (typeof v === 'string' && v.startsWith('https://id.solsynth.dev/api')) {
out[k] = v.replace('https://id.solsynth.dev/api', `${config.public.solarNetworkApi}/cgi/id`)
}
return {
"authorization_endpoint": `${config.public.siteUrl}/auth/authorize`,
"grant_types_supported": [
"authorization_code",
"implicit",
"refresh_token",
],
"id_token_signing_alg_values_supported": [
"HS512",
],
"issuer": config.public.siteUrl,
"response_types_supported": [
"code",
"token",
],
"subject_types_supported": [
"public",
],
"token_endpoint": `${config.public.solarNetworkApi}/cgi/id/auth/token`,
"token_endpoint_auth_methods_supported": [
"client_secret_post",
],
"token_endpoint_auth_signing_alg_values_supported": [
"HS512",
],
"userinfo_endpoint": `${config.public.solarNetworkApi}/cgi/id/users/me`,
}
return out
})
})