feat: add basic webui
This commit is contained in:
15
apps/api/.env.development
Normal file
15
apps/api/.env.development
Normal file
@@ -0,0 +1,15 @@
|
||||
# Server
|
||||
BETTER_AUTH_SECRET="konobangu"
|
||||
DATABASE_URL="postgres://konobangu:konobangu@127.0.0.1:5432/konobangu"
|
||||
BETTERSTACK_API_KEY=""
|
||||
BETTERSTACK_URL=""
|
||||
FLAGS_SECRET=""
|
||||
ARCJET_KEY=""
|
||||
SVIX_TOKEN=""
|
||||
LIVEBLOCKS_SECRET=""
|
||||
|
||||
# Client
|
||||
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
||||
NEXT_PUBLIC_WEB_URL="http://localhost:3001"
|
||||
NEXT_PUBLIC_DOCS_URL="http://localhost:3004"
|
||||
NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="https://webui.konobangu.com"
|
||||
15
apps/api/.env.example
Normal file
15
apps/api/.env.example
Normal file
@@ -0,0 +1,15 @@
|
||||
# Server
|
||||
BETTER_AUTH_SECRET=""
|
||||
DATABASE_URL=""
|
||||
BETTERSTACK_API_KEY=""
|
||||
BETTERSTACK_URL=""
|
||||
FLAGS_SECRET=""
|
||||
ARCJET_KEY=""
|
||||
SVIX_TOKEN=""
|
||||
LIVEBLOCKS_SECRET=""
|
||||
|
||||
# Client
|
||||
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
||||
NEXT_PUBLIC_WEB_URL="http://localhost:3001"
|
||||
NEXT_PUBLIC_DOCS_URL="http://localhost:3004"
|
||||
NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL="http://localhost:3000"
|
||||
45
apps/api/.gitignore
vendored
Normal file
45
apps/api/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# prisma
|
||||
.env
|
||||
|
||||
# react.email
|
||||
.react-email
|
||||
|
||||
# Sentry
|
||||
.sentryclirc
|
||||
BIN
apps/api/app/apple-icon.png
Normal file
BIN
apps/api/app/apple-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 216 B |
17
apps/api/app/cron/keep-alive/route.ts
Normal file
17
apps/api/app/cron/keep-alive/route.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { database } from '@konobangu/database';
|
||||
|
||||
export const POST = async () => {
|
||||
const newPage = await database
|
||||
.insertInto('page')
|
||||
.values([
|
||||
{
|
||||
name: 'cron-temp',
|
||||
},
|
||||
])
|
||||
.returning('id')
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
await database.deleteFrom('page').where('id', '=', newPage.id);
|
||||
|
||||
return new Response('OK', { status: 200 });
|
||||
};
|
||||
29
apps/api/app/global-error.tsx
Normal file
29
apps/api/app/global-error.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@konobangu/design-system/components/ui/button';
|
||||
import { fonts } from '@konobangu/design-system/lib/fonts';
|
||||
import { captureException } from '@sentry/nextjs';
|
||||
import type NextError from 'next/error';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
type GlobalErrorProperties = {
|
||||
readonly error: NextError & { digest?: string };
|
||||
readonly reset: () => void;
|
||||
};
|
||||
|
||||
const GlobalError = ({ error, reset }: GlobalErrorProperties) => {
|
||||
useEffect(() => {
|
||||
captureException(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<html lang="en" className={fonts}>
|
||||
<body>
|
||||
<h1>Oops, something went wrong</h1>
|
||||
<Button onClick={() => reset()}>Try again</Button>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
};
|
||||
|
||||
export default GlobalError;
|
||||
3
apps/api/app/health/route.ts
Normal file
3
apps/api/app/health/route.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const runtime = 'edge';
|
||||
|
||||
export const GET = (): Response => new Response('OK', { status: 200 });
|
||||
BIN
apps/api/app/icon.png
Normal file
BIN
apps/api/app/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 B |
13
apps/api/app/layout.tsx
Normal file
13
apps/api/app/layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type RootLayoutProperties = {
|
||||
readonly children: ReactNode;
|
||||
};
|
||||
|
||||
const RootLayout = ({ children }: RootLayoutProperties) => (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
export default RootLayout;
|
||||
BIN
apps/api/app/opengraph-image.png
Normal file
BIN
apps/api/app/opengraph-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
3
apps/api/instrumentation.ts
Normal file
3
apps/api/instrumentation.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { initializeSentry } from '@konobangu/next-config/instrumentation';
|
||||
|
||||
export const register = initializeSentry();
|
||||
15
apps/api/next.config.ts
Normal file
15
apps/api/next.config.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { env } from '@konobangu/env';
|
||||
import { config, withAnalyzer, withSentry } from '@konobangu/next-config';
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
let nextConfig: NextConfig = { ...config };
|
||||
|
||||
if (env.VERCEL) {
|
||||
nextConfig = withSentry(nextConfig);
|
||||
}
|
||||
|
||||
if (env.ANALYZE === 'true') {
|
||||
nextConfig = withAnalyzer(nextConfig);
|
||||
}
|
||||
|
||||
export default nextConfig;
|
||||
37
apps/api/package.json
Normal file
37
apps/api/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "api",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "concurrently \"pnpm:next\"",
|
||||
"next": "next dev -p 3002 --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"analyze": "ANALYZE=true pnpm build",
|
||||
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
||||
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
|
||||
},
|
||||
"dependencies": {
|
||||
"@konobangu/analytics": "workspace:*",
|
||||
"@konobangu/auth": "workspace:*",
|
||||
"@konobangu/database": "workspace:*",
|
||||
"@konobangu/design-system": "workspace:*",
|
||||
"@konobangu/env": "workspace:*",
|
||||
"@konobangu/next-config": "workspace:*",
|
||||
"@konobangu/observability": "workspace:*",
|
||||
"@sentry/nextjs": "^8.43.0",
|
||||
"import-in-the-middle": "^1.11.3",
|
||||
"next": "^15.1.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"require-in-the-middle": "^7.4.0",
|
||||
"svix": "^1.43.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@konobangu/typescript-config": "workspace:*",
|
||||
"@types/node": "22.10.1",
|
||||
"@types/react": "19.0.1",
|
||||
"@types/react-dom": "19.0.2",
|
||||
"concurrently": "^9.1.0",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
}
|
||||
34
apps/api/sentry.client.config.ts
Normal file
34
apps/api/sentry.client.config.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file configures the initialization of Sentry on the client.
|
||||
* The config you add here will be used whenever a users loads a page in their browser.
|
||||
* https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
*/
|
||||
|
||||
import { init, replayIntegration } from '@sentry/nextjs';
|
||||
|
||||
init({
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 1,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
|
||||
replaysOnErrorSampleRate: 1,
|
||||
|
||||
/*
|
||||
* This sets the sample rate to be 10%. You may want this to be 100% while
|
||||
* in development and sample at a lower rate in production
|
||||
*/
|
||||
replaysSessionSampleRate: 0.1,
|
||||
|
||||
// You can remove this option if you're not planning to use the Sentry Session Replay feature:
|
||||
integrations: [
|
||||
replayIntegration({
|
||||
// Additional Replay configuration goes in here, for example:
|
||||
maskAllText: true,
|
||||
blockAllMedia: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
17
apps/api/tsconfig.json
Normal file
17
apps/api/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "@konobangu/typescript-config/nextjs.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"],
|
||||
"@konobangu/*": ["../../packages/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"next.config.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
]
|
||||
}
|
||||
8
apps/api/vercel.json
Normal file
8
apps/api/vercel.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"crons": [
|
||||
{
|
||||
"path": "/cron/keep-alive",
|
||||
"schedule": "0 1 * * *"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user