@jonsoc/console-app 1.1.34

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.
Files changed (217) hide show
  1. package/.opencode/agent/css.md +149 -0
  2. package/README.md +32 -0
  3. package/package.json +49 -0
  4. package/public/apple-touch-icon-v3.png +1 -0
  5. package/public/apple-touch-icon.png +1 -0
  6. package/public/email +1 -0
  7. package/public/favicon-96x96-v3.png +1 -0
  8. package/public/favicon-96x96.png +1 -0
  9. package/public/favicon-v3.ico +1 -0
  10. package/public/favicon-v3.svg +1 -0
  11. package/public/favicon.ico +1 -0
  12. package/public/favicon.svg +1 -0
  13. package/public/opencode-brand-assets.zip +0 -0
  14. package/public/robots.txt +6 -0
  15. package/public/site.webmanifest +1 -0
  16. package/public/social-share-black.png +1 -0
  17. package/public/social-share-zen.png +1 -0
  18. package/public/social-share.png +1 -0
  19. package/public/theme.json +182 -0
  20. package/public/web-app-manifest-192x192.png +1 -0
  21. package/public/web-app-manifest-512x512.png +1 -0
  22. package/script/generate-sitemap.ts +103 -0
  23. package/src/app.css +1 -0
  24. package/src/app.tsx +27 -0
  25. package/src/asset/black/hero.png +0 -0
  26. package/src/asset/brand/opencode-brand-assets.zip +0 -0
  27. package/src/asset/brand/opencode-logo-dark.png +0 -0
  28. package/src/asset/brand/opencode-logo-dark.svg +16 -0
  29. package/src/asset/brand/opencode-logo-light.png +0 -0
  30. package/src/asset/brand/opencode-logo-light.svg +16 -0
  31. package/src/asset/brand/opencode-wordmark-dark.png +0 -0
  32. package/src/asset/brand/opencode-wordmark-dark.svg +30 -0
  33. package/src/asset/brand/opencode-wordmark-light.png +0 -0
  34. package/src/asset/brand/opencode-wordmark-light.svg +30 -0
  35. package/src/asset/brand/opencode-wordmark-simple-dark.png +0 -0
  36. package/src/asset/brand/opencode-wordmark-simple-dark.svg +22 -0
  37. package/src/asset/brand/opencode-wordmark-simple-light.png +0 -0
  38. package/src/asset/brand/opencode-wordmark-simple-light.svg +22 -0
  39. package/src/asset/brand/preview-opencode-dark.png +0 -0
  40. package/src/asset/brand/preview-opencode-logo-dark.png +0 -0
  41. package/src/asset/brand/preview-opencode-logo-light.png +0 -0
  42. package/src/asset/brand/preview-opencode-wordmark-dark.png +0 -0
  43. package/src/asset/brand/preview-opencode-wordmark-light.png +0 -0
  44. package/src/asset/brand/preview-opencode-wordmark-simple-dark.png +0 -0
  45. package/src/asset/brand/preview-opencode-wordmark-simple-light.png +0 -0
  46. package/src/asset/lander/avatar-adam.png +0 -0
  47. package/src/asset/lander/avatar-david.png +0 -0
  48. package/src/asset/lander/avatar-dax.png +0 -0
  49. package/src/asset/lander/avatar-frank.png +0 -0
  50. package/src/asset/lander/avatar-jay.png +0 -0
  51. package/src/asset/lander/brand-assets-dark.svg +10 -0
  52. package/src/asset/lander/brand-assets-light.svg +10 -0
  53. package/src/asset/lander/brand.png +0 -0
  54. package/src/asset/lander/check.svg +3 -0
  55. package/src/asset/lander/copy.svg +3 -0
  56. package/src/asset/lander/desktop-app-icon.png +0 -0
  57. package/src/asset/lander/dock.png +0 -0
  58. package/src/asset/lander/logo-dark.svg +11 -0
  59. package/src/asset/lander/logo-light.svg +11 -0
  60. package/src/asset/lander/opencode-comparison-min.mp4 +0 -0
  61. package/src/asset/lander/opencode-comparison-poster.png +0 -0
  62. package/src/asset/lander/opencode-desktop-icon.png +0 -0
  63. package/src/asset/lander/opencode-logo-dark.svg +11 -0
  64. package/src/asset/lander/opencode-logo-light.svg +11 -0
  65. package/src/asset/lander/opencode-min.mp4 +0 -0
  66. package/src/asset/lander/opencode-poster.png +0 -0
  67. package/src/asset/lander/opencode-wordmark-dark.svg +25 -0
  68. package/src/asset/lander/opencode-wordmark-light.svg +25 -0
  69. package/src/asset/lander/screenshot-github.png +0 -0
  70. package/src/asset/lander/screenshot-splash.png +0 -0
  71. package/src/asset/lander/screenshot-vscode.png +0 -0
  72. package/src/asset/lander/screenshot.png +0 -0
  73. package/src/asset/lander/wordmark-dark.svg +3 -0
  74. package/src/asset/lander/wordmark-light.svg +3 -0
  75. package/src/asset/logo-ornate-dark.svg +18 -0
  76. package/src/asset/logo-ornate-light.svg +18 -0
  77. package/src/asset/logo.svg +18 -0
  78. package/src/asset/zen-ornate-dark.svg +8 -0
  79. package/src/asset/zen-ornate-light.svg +8 -0
  80. package/src/component/dropdown.css +80 -0
  81. package/src/component/dropdown.tsx +79 -0
  82. package/src/component/email-signup.tsx +48 -0
  83. package/src/component/faq.tsx +33 -0
  84. package/src/component/footer.tsx +38 -0
  85. package/src/component/header-context-menu.css +63 -0
  86. package/src/component/header.tsx +279 -0
  87. package/src/component/icon.tsx +257 -0
  88. package/src/component/legal.tsx +20 -0
  89. package/src/component/modal.css +66 -0
  90. package/src/component/modal.tsx +24 -0
  91. package/src/component/spotlight.css +15 -0
  92. package/src/component/spotlight.tsx +820 -0
  93. package/src/config.ts +29 -0
  94. package/src/context/auth.session.ts +0 -0
  95. package/src/context/auth.ts +116 -0
  96. package/src/context/auth.withActor.ts +7 -0
  97. package/src/entry-client.tsx +4 -0
  98. package/src/entry-server.tsx +30 -0
  99. package/src/global.d.ts +5 -0
  100. package/src/lib/github.ts +38 -0
  101. package/src/middleware.ts +5 -0
  102. package/src/routes/[...404].css +130 -0
  103. package/src/routes/[...404].tsx +38 -0
  104. package/src/routes/api/enterprise.ts +47 -0
  105. package/src/routes/auth/[...callback].ts +41 -0
  106. package/src/routes/auth/authorize.ts +10 -0
  107. package/src/routes/auth/index.ts +12 -0
  108. package/src/routes/auth/logout.ts +17 -0
  109. package/src/routes/auth/status.ts +7 -0
  110. package/src/routes/bench/[id].tsx +365 -0
  111. package/src/routes/bench/index.tsx +86 -0
  112. package/src/routes/bench/submission.ts +29 -0
  113. package/src/routes/black/common.tsx +62 -0
  114. package/src/routes/black/index.tsx +108 -0
  115. package/src/routes/black/subscribe/[plan].tsx +449 -0
  116. package/src/routes/black/workspace.css +214 -0
  117. package/src/routes/black/workspace.tsx +229 -0
  118. package/src/routes/black.css +828 -0
  119. package/src/routes/black.tsx +285 -0
  120. package/src/routes/brand/index.css +555 -0
  121. package/src/routes/brand/index.tsx +252 -0
  122. package/src/routes/changelog/index.css +477 -0
  123. package/src/routes/changelog/index.tsx +147 -0
  124. package/src/routes/debug/index.ts +13 -0
  125. package/src/routes/desktop-feedback.ts +5 -0
  126. package/src/routes/discord.ts +5 -0
  127. package/src/routes/docs/[...path].ts +20 -0
  128. package/src/routes/docs/index.ts +20 -0
  129. package/src/routes/download/[platform].ts +38 -0
  130. package/src/routes/download/index.css +750 -0
  131. package/src/routes/download/index.tsx +482 -0
  132. package/src/routes/download/types.ts +4 -0
  133. package/src/routes/enterprise/index.css +578 -0
  134. package/src/routes/enterprise/index.tsx +251 -0
  135. package/src/routes/index.css +1251 -0
  136. package/src/routes/index.tsx +840 -0
  137. package/src/routes/legal/privacy-policy/index.css +343 -0
  138. package/src/routes/legal/privacy-policy/index.tsx +1512 -0
  139. package/src/routes/legal/terms-of-service/index.css +254 -0
  140. package/src/routes/legal/terms-of-service/index.tsx +512 -0
  141. package/src/routes/openapi.json.ts +7 -0
  142. package/src/routes/s/[id].ts +20 -0
  143. package/src/routes/stripe/webhook.ts +532 -0
  144. package/src/routes/t/[...path].tsx +20 -0
  145. package/src/routes/temp.tsx +172 -0
  146. package/src/routes/user-menu.css +18 -0
  147. package/src/routes/user-menu.tsx +32 -0
  148. package/src/routes/workspace/[id]/billing/billing-section.module.css +185 -0
  149. package/src/routes/workspace/[id]/billing/billing-section.tsx +240 -0
  150. package/src/routes/workspace/[id]/billing/black-section.module.css +142 -0
  151. package/src/routes/workspace/[id]/billing/black-section.tsx +269 -0
  152. package/src/routes/workspace/[id]/billing/black-waitlist-section.module.css +23 -0
  153. package/src/routes/workspace/[id]/billing/index.tsx +32 -0
  154. package/src/routes/workspace/[id]/billing/monthly-limit-section.module.css +96 -0
  155. package/src/routes/workspace/[id]/billing/monthly-limit-section.tsx +133 -0
  156. package/src/routes/workspace/[id]/billing/payment-section.module.css +93 -0
  157. package/src/routes/workspace/[id]/billing/payment-section.tsx +122 -0
  158. package/src/routes/workspace/[id]/billing/reload-section.module.css +261 -0
  159. package/src/routes/workspace/[id]/billing/reload-section.tsx +213 -0
  160. package/src/routes/workspace/[id]/graph-section.module.css +145 -0
  161. package/src/routes/workspace/[id]/graph-section.tsx +475 -0
  162. package/src/routes/workspace/[id]/index.tsx +81 -0
  163. package/src/routes/workspace/[id]/keys/index.tsx +11 -0
  164. package/src/routes/workspace/[id]/keys/key-section.module.css +197 -0
  165. package/src/routes/workspace/[id]/keys/key-section.tsx +176 -0
  166. package/src/routes/workspace/[id]/members/index.tsx +11 -0
  167. package/src/routes/workspace/[id]/members/member-section.module.css +249 -0
  168. package/src/routes/workspace/[id]/members/member-section.tsx +343 -0
  169. package/src/routes/workspace/[id]/members/role-dropdown.css +72 -0
  170. package/src/routes/workspace/[id]/members/role-dropdown.tsx +43 -0
  171. package/src/routes/workspace/[id]/model-section.module.css +173 -0
  172. package/src/routes/workspace/[id]/model-section.tsx +174 -0
  173. package/src/routes/workspace/[id]/new-user-section.module.css +143 -0
  174. package/src/routes/workspace/[id]/new-user-section.tsx +104 -0
  175. package/src/routes/workspace/[id]/provider-section.module.css +138 -0
  176. package/src/routes/workspace/[id]/provider-section.tsx +188 -0
  177. package/src/routes/workspace/[id]/settings/index.tsx +11 -0
  178. package/src/routes/workspace/[id]/settings/settings-section.module.css +94 -0
  179. package/src/routes/workspace/[id]/settings/settings-section.tsx +122 -0
  180. package/src/routes/workspace/[id]/usage-section.module.css +185 -0
  181. package/src/routes/workspace/[id]/usage-section.tsx +200 -0
  182. package/src/routes/workspace/[id].css +308 -0
  183. package/src/routes/workspace/[id].tsx +62 -0
  184. package/src/routes/workspace/common.tsx +120 -0
  185. package/src/routes/workspace-picker.css +74 -0
  186. package/src/routes/workspace-picker.tsx +122 -0
  187. package/src/routes/workspace.css +107 -0
  188. package/src/routes/workspace.tsx +38 -0
  189. package/src/routes/zen/index.css +866 -0
  190. package/src/routes/zen/index.tsx +343 -0
  191. package/src/routes/zen/util/dataDumper.ts +44 -0
  192. package/src/routes/zen/util/error.ts +13 -0
  193. package/src/routes/zen/util/handler.ts +784 -0
  194. package/src/routes/zen/util/logger.ts +12 -0
  195. package/src/routes/zen/util/provider/anthropic.ts +752 -0
  196. package/src/routes/zen/util/provider/google.ts +75 -0
  197. package/src/routes/zen/util/provider/openai-compatible.ts +546 -0
  198. package/src/routes/zen/util/provider/openai.ts +630 -0
  199. package/src/routes/zen/util/provider/provider.ts +210 -0
  200. package/src/routes/zen/util/rateLimiter.ts +41 -0
  201. package/src/routes/zen/util/stickyProviderTracker.ts +16 -0
  202. package/src/routes/zen/util/trialLimiter.ts +49 -0
  203. package/src/routes/zen/v1/chat/completions.ts +11 -0
  204. package/src/routes/zen/v1/messages.ts +11 -0
  205. package/src/routes/zen/v1/models/[model].ts +13 -0
  206. package/src/routes/zen/v1/models.ts +60 -0
  207. package/src/routes/zen/v1/responses.ts +11 -0
  208. package/src/style/base.css +21 -0
  209. package/src/style/component/button.css +102 -0
  210. package/src/style/index.css +8 -0
  211. package/src/style/reset.css +76 -0
  212. package/src/style/token/color.css +91 -0
  213. package/src/style/token/font.css +21 -0
  214. package/src/style/token/space.css +46 -0
  215. package/sst-env.d.ts +9 -0
  216. package/tsconfig.json +21 -0
  217. package/vite.config.ts +25 -0
package/src/config.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Application-wide constants and configuration
3
+ */
4
+ export const config = {
5
+ // Base URL
6
+ baseUrl: "https://jonsoc.com",
7
+
8
+ // GitHub
9
+ github: {
10
+ repoUrl: "https://github.com/Noisemaker111/Jonsoc",
11
+ starsFormatted: {
12
+ compact: "80K",
13
+ full: "80,000",
14
+ },
15
+ },
16
+
17
+ // Social links
18
+ social: {
19
+ twitter: "https://x.com/jonsoc",
20
+ discord: "https://discord.gg/jonsoc",
21
+ },
22
+
23
+ // Static stats (used on landing page)
24
+ stats: {
25
+ contributors: "600",
26
+ commits: "7,500",
27
+ monthlyUsers: "1.5M",
28
+ },
29
+ } as const
File without changes
@@ -0,0 +1,116 @@
1
+ import { getRequestEvent } from "solid-js/web"
2
+ import { and, Database, eq, inArray, isNull, sql } from "@jonsoc/console-core/drizzle/index.js"
3
+ import { UserTable } from "@jonsoc/console-core/schema/user.sql.js"
4
+ import { redirect } from "@solidjs/router"
5
+ import { Actor } from "@jonsoc/console-core/actor.js"
6
+
7
+ import { createClient } from "@openauthjs/openauth/client"
8
+
9
+ export const AuthClient = createClient({
10
+ clientID: "app",
11
+ issuer: import.meta.env.VITE_AUTH_URL,
12
+ })
13
+
14
+ import { useSession } from "@solidjs/start/http"
15
+ import { Resource } from "@jonsoc/console-resource"
16
+
17
+ export interface AuthSession {
18
+ account?: Record<
19
+ string,
20
+ {
21
+ id: string
22
+ email: string
23
+ }
24
+ >
25
+ current?: string
26
+ }
27
+
28
+ export function useAuthSession() {
29
+ return useSession<AuthSession>({
30
+ password: Resource.ZEN_SESSION_SECRET.value,
31
+ name: "auth",
32
+ maxAge: 60 * 60 * 24 * 365,
33
+ cookie: {
34
+ secure: false,
35
+ httpOnly: true,
36
+ },
37
+ })
38
+ }
39
+
40
+ export const getActor = async (workspace?: string): Promise<Actor.Info> => {
41
+ "use server"
42
+ const evt = getRequestEvent()
43
+ if (!evt) throw new Error("No request event")
44
+ if (evt.locals.actor) return evt.locals.actor
45
+ evt.locals.actor = (async () => {
46
+ const auth = await useAuthSession()
47
+ if (!workspace) {
48
+ const account = auth.data.account ?? {}
49
+ const current = account[auth.data.current ?? ""]
50
+ if (current) {
51
+ return {
52
+ type: "account",
53
+ properties: {
54
+ email: current.email,
55
+ accountID: current.id,
56
+ },
57
+ }
58
+ }
59
+ if (Object.keys(account).length > 0) {
60
+ const current = Object.values(account)[0]
61
+ await auth.update((val) => ({
62
+ ...val,
63
+ current: current.id,
64
+ }))
65
+ return {
66
+ type: "account",
67
+ properties: {
68
+ email: current.email,
69
+ accountID: current.id,
70
+ },
71
+ }
72
+ }
73
+ return {
74
+ type: "public",
75
+ properties: {},
76
+ }
77
+ }
78
+ const accounts = Object.keys(auth.data.account ?? {})
79
+ if (accounts.length) {
80
+ const user = await Database.use((tx) =>
81
+ tx
82
+ .select()
83
+ .from(UserTable)
84
+ .where(
85
+ and(
86
+ eq(UserTable.workspaceID, workspace),
87
+ isNull(UserTable.timeDeleted),
88
+ inArray(UserTable.accountID, accounts),
89
+ ),
90
+ )
91
+ .limit(1)
92
+ .execute()
93
+ .then((x) => x[0]),
94
+ )
95
+ if (user) {
96
+ await Database.use((tx) =>
97
+ tx
98
+ .update(UserTable)
99
+ .set({ timeSeen: sql`now()` })
100
+ .where(and(eq(UserTable.workspaceID, workspace), eq(UserTable.id, user.id))),
101
+ )
102
+ return {
103
+ type: "user",
104
+ properties: {
105
+ userID: user.id,
106
+ workspaceID: user.workspaceID,
107
+ accountID: user.accountID,
108
+ role: user.role,
109
+ },
110
+ }
111
+ }
112
+ }
113
+ throw redirect("/auth/authorize")
114
+ })()
115
+ return evt.locals.actor
116
+ }
@@ -0,0 +1,7 @@
1
+ import { Actor } from "@jonsoc/console-core/actor.js"
2
+ import { getActor } from "./auth"
3
+
4
+ export async function withActor<T>(fn: () => T, workspace?: string) {
5
+ const actor = await getActor(workspace)
6
+ return Actor.provide(actor.type, actor.properties, fn)
7
+ }
@@ -0,0 +1,4 @@
1
+ // @refresh reload
2
+ import { mount, StartClient } from "@solidjs/start/client"
3
+
4
+ mount(() => <StartClient />, document.getElementById("app")!)
@@ -0,0 +1,30 @@
1
+ // @refresh reload
2
+ import { createHandler, StartServer } from "@solidjs/start/server"
3
+
4
+ const criticalCSS = `[data-component="top"]{min-height:80px;display:flex;align-items:center}`
5
+
6
+ export default createHandler(
7
+ () => (
8
+ <StartServer
9
+ document={({ assets, children, scripts }) => (
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8" />
13
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
14
+ <meta property="og:image" content="/social-share.png" />
15
+ <meta property="twitter:image" content="/social-share.png" />
16
+ <style>{criticalCSS}</style>
17
+ {assets}
18
+ </head>
19
+ <body>
20
+ <div id="app">{children}</div>
21
+ {scripts}
22
+ </body>
23
+ </html>
24
+ )}
25
+ />
26
+ ),
27
+ {
28
+ mode: "async",
29
+ },
30
+ )
@@ -0,0 +1,5 @@
1
+ /// <reference types="@solidjs/start/env" />
2
+
3
+ export declare module "@solidjs/start/server" {
4
+ export type APIEvent = { request: Request }
5
+ }
@@ -0,0 +1,38 @@
1
+ import { query } from "@solidjs/router"
2
+ import { config } from "~/config"
3
+
4
+ export const github = query(async () => {
5
+ "use server"
6
+ const headers = {
7
+ "User-Agent":
8
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
9
+ }
10
+ const apiBaseUrl = config.github.repoUrl.replace("https://github.com/", "https://api.github.com/repos/")
11
+ try {
12
+ const [meta, releases, contributors] = await Promise.all([
13
+ fetch(apiBaseUrl, { headers }).then((res) => res.json()),
14
+ fetch(`${apiBaseUrl}/releases`, { headers }).then((res) => res.json()),
15
+ fetch(`${apiBaseUrl}/contributors?per_page=1`, { headers }),
16
+ ])
17
+ if (!Array.isArray(releases) || releases.length === 0) {
18
+ return undefined
19
+ }
20
+ const [release] = releases
21
+ const linkHeader = contributors.headers.get("Link")
22
+ const contributorCount = linkHeader
23
+ ? Number.parseInt(linkHeader.match(/&page=(\d+)>; rel="last"/)?.at(1) ?? "0")
24
+ : 0
25
+ return {
26
+ stars: meta.stargazers_count,
27
+ release: {
28
+ name: release.name,
29
+ url: release.html_url,
30
+ tag_name: release.tag_name,
31
+ },
32
+ contributors: contributorCount,
33
+ }
34
+ } catch (e) {
35
+ console.error(e)
36
+ }
37
+ return undefined
38
+ }, "github")
@@ -0,0 +1,5 @@
1
+ import { createMiddleware } from "@solidjs/start/middleware"
2
+
3
+ export default createMiddleware({
4
+ onBeforeResponse() {},
5
+ })
@@ -0,0 +1,130 @@
1
+ [data-page="not-found"] {
2
+ --color-text: hsl(224, 10%, 10%);
3
+ --color-text-secondary: hsl(224, 7%, 46%);
4
+ --color-text-dimmed: hsl(224, 6%, 63%);
5
+ --color-text-inverted: hsl(0, 0%, 100%);
6
+
7
+ --color-border: hsl(224, 6%, 77%);
8
+ }
9
+
10
+ [data-page="not-found"] {
11
+ @media (prefers-color-scheme: dark) {
12
+ --color-text: hsl(0, 0%, 100%);
13
+ --color-text-secondary: hsl(224, 6%, 66%);
14
+ --color-text-dimmed: hsl(224, 7%, 46%);
15
+ --color-text-inverted: hsl(224, 10%, 10%);
16
+
17
+ --color-border: hsl(224, 6%, 36%);
18
+ }
19
+ }
20
+
21
+ [data-page="not-found"] {
22
+ --padding: 3rem;
23
+ --vertical-padding: 1.5rem;
24
+ --heading-font-size: 1.375rem;
25
+
26
+ @media (max-width: 30rem) {
27
+ --padding: 1rem;
28
+ --vertical-padding: 0.75rem;
29
+ --heading-font-size: 1rem;
30
+ }
31
+
32
+ font-family: var(--font-mono);
33
+ color: var(--color-text);
34
+ padding: calc(var(--padding) + 1rem);
35
+ min-height: 100vh;
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+
40
+ a {
41
+ color: var(--color-text);
42
+ text-decoration: underline;
43
+ text-underline-offset: var(--space-0-75);
44
+ text-decoration-thickness: 1px;
45
+ }
46
+
47
+ [data-component="content"] {
48
+ max-width: 40rem;
49
+ width: 100%;
50
+ border: 1px solid var(--color-border);
51
+ }
52
+
53
+ [data-component="top"] {
54
+ padding: var(--padding);
55
+ display: flex;
56
+ flex-direction: column;
57
+ align-items: center;
58
+ gap: calc(var(--vertical-padding) / 2);
59
+ text-align: center;
60
+
61
+ [data-slot="logo-link"] {
62
+ text-decoration: none;
63
+ }
64
+
65
+ img {
66
+ height: auto;
67
+ width: clamp(200px, 85vw, 400px);
68
+ }
69
+
70
+ [data-slot="logo dark"] {
71
+ display: none;
72
+ }
73
+
74
+ @media (prefers-color-scheme: dark) {
75
+ [data-slot="logo light"] {
76
+ display: none;
77
+ }
78
+ [data-slot="logo dark"] {
79
+ display: block;
80
+ }
81
+ }
82
+
83
+ [data-slot="title"] {
84
+ line-height: 1.25;
85
+ font-weight: 500;
86
+ text-align: center;
87
+ font-size: var(--heading-font-size);
88
+ color: var(--color-text);
89
+ text-transform: uppercase;
90
+ margin: 0;
91
+ }
92
+ }
93
+
94
+ [data-component="actions"] {
95
+ border-top: 1px solid var(--color-border);
96
+ display: flex;
97
+
98
+ [data-slot="action"] {
99
+ flex: 1;
100
+ text-align: center;
101
+ line-height: 1.4;
102
+ padding: var(--vertical-padding) 1rem;
103
+ text-transform: uppercase;
104
+ font-size: 1rem;
105
+
106
+ a {
107
+ display: block;
108
+ width: 100%;
109
+ height: 100%;
110
+ color: var(--color-text);
111
+ text-decoration: underline;
112
+ text-underline-offset: var(--space-0-75);
113
+ text-decoration-thickness: 1px;
114
+ }
115
+ }
116
+
117
+ [data-slot="action"] + [data-slot="action"] {
118
+ border-left: 1px solid var(--color-border);
119
+ }
120
+
121
+ @media (max-width: 40rem) {
122
+ flex-direction: column;
123
+
124
+ [data-slot="action"] + [data-slot="action"] {
125
+ border-left: none;
126
+ border-top: 1px solid var(--color-border);
127
+ }
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,38 @@
1
+ import "./[...404].css"
2
+ import { Title } from "@solidjs/meta"
3
+ import { HttpStatusCode } from "@solidjs/start"
4
+ import logoLight from "../asset/logo-ornate-light.svg"
5
+ import logoDark from "../asset/logo-ornate-dark.svg"
6
+
7
+ export default function NotFound() {
8
+ return (
9
+ <main data-page="not-found">
10
+ <Title>Not Found | jonsoc</Title>
11
+ <HttpStatusCode code={404} />
12
+ <div data-component="content">
13
+ <section data-component="top">
14
+ <a href="/" data-slot="logo-link">
15
+ <img data-slot="logo light" src={logoLight} alt="jonsoc logo light" />
16
+ <img data-slot="logo dark" src={logoDark} alt="jonsoc logo dark" />
17
+ </a>
18
+ <h1 data-slot="title">404 - Page Not Found</h1>
19
+ </section>
20
+
21
+ <section data-component="actions">
22
+ <div data-slot="action">
23
+ <a href="/">Home</a>
24
+ </div>
25
+ <div data-slot="action">
26
+ <a href="/docs">Docs</a>
27
+ </div>
28
+ <div data-slot="action">
29
+ <a href="https://github.com/anomalyco/jonsoc">GitHub</a>
30
+ </div>
31
+ <div data-slot="action">
32
+ <a href="/discord">Discord</a>
33
+ </div>
34
+ </section>
35
+ </div>
36
+ </main>
37
+ )
38
+ }
@@ -0,0 +1,47 @@
1
+ import type { APIEvent } from "@solidjs/start/server"
2
+ import { AWS } from "@jonsoc/console-core/aws.js"
3
+
4
+ interface EnterpriseFormData {
5
+ name: string
6
+ role: string
7
+ email: string
8
+ message: string
9
+ }
10
+
11
+ export async function POST(event: APIEvent) {
12
+ try {
13
+ const body = (await event.request.json()) as EnterpriseFormData
14
+
15
+ // Validate required fields
16
+ if (!body.name || !body.role || !body.email || !body.message) {
17
+ return Response.json({ error: "All fields are required" }, { status: 400 })
18
+ }
19
+
20
+ // Validate email format
21
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
22
+ if (!emailRegex.test(body.email)) {
23
+ return Response.json({ error: "Invalid email format" }, { status: 400 })
24
+ }
25
+
26
+ // Create email content
27
+ const emailContent = `
28
+ ${body.message}<br><br>
29
+ --<br>
30
+ ${body.name}<br>
31
+ ${body.role}<br>
32
+ ${body.email}`.trim()
33
+
34
+ // Send email using AWS SES
35
+ await AWS.sendEmail({
36
+ to: "contact@anoma.ly",
37
+ subject: `Enterprise Inquiry from ${body.name}`,
38
+ body: emailContent,
39
+ replyTo: body.email,
40
+ })
41
+
42
+ return Response.json({ success: true, message: "Form submitted successfully" }, { status: 200 })
43
+ } catch (error) {
44
+ console.error("Error processing enterprise form:", error)
45
+ return Response.json({ error: "Internal server error" }, { status: 500 })
46
+ }
47
+ }
@@ -0,0 +1,41 @@
1
+ import { redirect } from "@solidjs/router"
2
+ import type { APIEvent } from "@solidjs/start/server"
3
+ import { AuthClient } from "~/context/auth"
4
+ import { useAuthSession } from "~/context/auth"
5
+
6
+ export async function GET(input: APIEvent) {
7
+ const url = new URL(input.request.url)
8
+
9
+ try {
10
+ const code = url.searchParams.get("code")
11
+ if (!code) throw new Error("No code found")
12
+ const result = await AuthClient.exchange(code, `${url.origin}${url.pathname}`)
13
+ if (result.err) throw new Error(result.err.message)
14
+ const decoded = AuthClient.decode(result.tokens.access, {} as any)
15
+ if (decoded.err) throw new Error(decoded.err.message)
16
+ const session = await useAuthSession()
17
+ const id = decoded.subject.properties.accountID
18
+ await session.update((value) => {
19
+ return {
20
+ ...value,
21
+ account: {
22
+ ...value.account,
23
+ [id]: {
24
+ id,
25
+ email: decoded.subject.properties.email,
26
+ },
27
+ },
28
+ current: id,
29
+ }
30
+ })
31
+ return redirect(url.pathname === "/auth/callback" ? "/auth" : url.pathname.replace("/auth/callback", ""))
32
+ } catch (e: any) {
33
+ return new Response(
34
+ JSON.stringify({
35
+ error: e.message,
36
+ cause: Object.fromEntries(url.searchParams.entries()),
37
+ }),
38
+ { status: 500 },
39
+ )
40
+ }
41
+ }
@@ -0,0 +1,10 @@
1
+ import type { APIEvent } from "@solidjs/start/server"
2
+ import { AuthClient } from "~/context/auth"
3
+
4
+ export async function GET(input: APIEvent) {
5
+ const url = new URL(input.request.url)
6
+ const cont = url.searchParams.get("continue") ?? ""
7
+ const callbackUrl = new URL(`./callback${cont}`, input.request.url)
8
+ const result = await AuthClient.authorize(callbackUrl.toString(), "code")
9
+ return Response.redirect(result.url, 302)
10
+ }
@@ -0,0 +1,12 @@
1
+ import { redirect } from "@solidjs/router"
2
+ import type { APIEvent } from "@solidjs/start/server"
3
+ import { getLastSeenWorkspaceID } from "../workspace/common"
4
+
5
+ export async function GET(input: APIEvent) {
6
+ try {
7
+ const workspaceID = await getLastSeenWorkspaceID()
8
+ return redirect(`/workspace/${workspaceID}`)
9
+ } catch {
10
+ return redirect("/auth/authorize")
11
+ }
12
+ }
@@ -0,0 +1,17 @@
1
+ import { redirect } from "@solidjs/router"
2
+ import { APIEvent } from "@solidjs/start"
3
+ import { useAuthSession } from "~/context/auth"
4
+
5
+ export async function GET(event: APIEvent) {
6
+ const auth = await useAuthSession()
7
+ const current = auth.data.current
8
+ if (current)
9
+ await auth.update((val) => {
10
+ delete val.account?.[current]
11
+ const first = Object.keys(val.account ?? {})[0]
12
+ val.current = first
13
+ event!.locals.actor = undefined
14
+ return val
15
+ })
16
+ return redirect("/zen")
17
+ }
@@ -0,0 +1,7 @@
1
+ import { APIEvent } from "@solidjs/start"
2
+ import { useAuthSession } from "~/context/auth"
3
+
4
+ export async function GET(input: APIEvent) {
5
+ const session = await useAuthSession()
6
+ return Response.json(session.data)
7
+ }