feat: add app-create and frp commands

This commit is contained in:
bipproduction
2025-10-07 16:26:23 +08:00
parent 5ef8241989
commit 1f71d34d97
3 changed files with 169 additions and 24 deletions

View File

@@ -71,6 +71,7 @@ if (!(await Bun.file(g3nConf).exists())) {
FRP_HOST=
FRP_USER=
FRP_SECRET=
FRP_AUTH_TOKEN=
`
Bun.write(g3nConf, conf);
console.log(`✅ G3N config created at ${g3nConf}`);

View File

@@ -60,32 +60,35 @@ export default function Home() {
`
const serverTemplate = `
import Elysia from "elysia";
import Swagger from "@elysiajs/swagger";
import html from "./index.html"
import Darmasaba from "./server/routes/darmasaba";
import apiAuth from "./server/middlewares/apiAuth";
const Docs = new Elysia({})
const Docs = new Elysia()
.use(Swagger({
path: "/docs",
}))
const Api = new Elysia({
prefix: "/api",
})
.use(Docs)
.post("/hello", () => "Hello, world!")
.use(apiAuth)
.use(Darmasaba)
const app = new Elysia()
.use(Api)
.get("/*", html)
.use(Docs)
.get("*", html)
.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});
export type Server = typeof app;
`
const notFoundTemplate = `
@@ -98,6 +101,104 @@ export default function NotFound() {
}
`
const prismaTemplate = `
import { PrismaClient } from 'generated/prisma'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = prisma
}
`
const apiAuthTemplate = `
//* eslint-disable @typescript-eslint/no-explicit-any */
import jwt, { type JWTPayloadSpec } from '@elysiajs/jwt'
import Elysia from 'elysia'
import { prisma } from '../lib/prisma'
const secret = process.env.JWT_SECRET
export default function apiAuth(app: Elysia) {
if (!secret) {
throw new Error('JWT_SECRET is not defined')
}
return app
.use(
jwt({
name: 'jwt',
secret,
})
)
.derive(async ({ cookie, headers, jwt }) => {
let token: string | undefined
if (cookie?.token?.value) {
token = cookie.token.value as any
}
if (headers['x-token']?.startsWith('Bearer ')) {
token = (headers['x-token'] as string).slice(7)
}
if (headers['authorization']?.startsWith('Bearer ')) {
token = (headers['authorization'] as string).slice(7)
}
let user: null | Awaited<ReturnType<typeof prisma.user.findUnique>> = null
if (token) {
try {
const decoded = (await jwt.verify(token)) as JWTPayloadSpec
if (decoded.sub) {
user = await prisma.user.findUnique({
where: { id: decoded.sub as string },
})
}
} catch (err) {
console.warn('[SERVER][apiAuth] Invalid token', err)
}
}
return { user }
})
.onBeforeHandle(({ user, set }) => {
if (!user) {
set.status = 401
return { error: 'Unauthorized' }
}
})
}
`
const envFileTemplate = (appName: string) => `
DATABASE_URL="postgresql://bip:Production_123@localhost:5432/${appName}?schema=public"
JWT_SECRET=super_sangat_rahasia_sekali
`
const prismaSchemaTemplate = `
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
password String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
`
const cmd = (appName: string) => `
@@ -105,45 +206,78 @@ bun init --react ${appName}
echo "init react"
cd ${appName}
echo "cd ${appName}"
echo "install dependencies"
bun add react-router-dom
echo "add react-router-dom"
bun add @mantine/core @mantine/hooks
echo "add @mantine/core @mantine/hooks"
bun add --dev postcss postcss-preset-mantine postcss-simple-vars
echo "add --dev postcss postcss-preset-mantine postcss-simple-vars"
bun add elysia @elysiajs/cors @elysiajs/swagger @elysiajs/eden
echo "add elysia @elysiajs/cors @elysiajs/swagger @elysiajs/eden"
bun add elysia @elysiajs/cors @elysiajs/swagger @elysiajs/eden @elysiajs/jwt prisma @prisma/client
echo "init prisma"
bun x prisma init
echo "generate file ..."
echo "generate postcss.config.js"
cat <<EOF > postcss.config.js
${postCssTemplate}
EOF
echo "postcss.config.js"
echo "generate src/App.tsx"
cat <<EOF > src/App.tsx
${appTemplate}
EOF
echo "src/App.tsx"
echo "generate src/AppRoutes.tsx"
cat <<EOF > src/AppRoutes.tsx
${appRoutesTemplate}
EOF
echo "src/AppRoutes.tsx"
echo "create dir src/pages"
mkdir src/pages
echo "mkdir src/pages"
echo "generate src/pages/Home.tsx"
cat <<EOF > src/pages/Home.tsx
${homeTemplate}
EOF
echo "src/pages/Home.tsx"
echo "generate src/index.tsx"
cat <<EOF > src/index.tsx
${serverTemplate}
EOF
echo "src/index.tsx"
echo "generate src/pages/NotFound.tsx"
cat <<EOF > src/pages/NotFound.tsx
${notFoundTemplate}
EOF
echo "src/pages/NotFound.tsx"
echo "create dir src/server/lib"
mkdir -p src/server/lib
echo "generate src/server/lib/prisma.ts"
cat <<EOF > src/server/lib/prisma.ts
${prismaTemplate}
EOF
echo "create dir server/middlewares"
mkdir -p server/middlewares
echo "generate server/middlewares/apiAuth.ts"
cat <<EOF > server/middlewares/apiAuth.ts
${apiAuthTemplate}
EOF
echo "generate .env"
cat <<EOF > .env
${envFileTemplate(appName)}
EOF
echo "generate prisma/schema.prisma"
cat <<EOF > prisma/schema.prisma
${prismaSchemaTemplate}
EOF
echo "remove src/APITester.tsx"
rm src/APITester.tsx
ls

View File

@@ -11,6 +11,7 @@ interface FrpConfig {
FRP_USER: string;
FRP_SECRET: string;
FRP_PROTO: string;
FRP_AUTH_TOKEN: string;
}
interface ProxyConf {
@@ -30,17 +31,19 @@ interface ProxyResponse {
proxies?: Proxy[];
}
const templateConfig = `
FRP_HOST=""
FRP_USER=""
FRP_SECRET=""
FRP_AUTH_TOKEN=""`;
async function ensureConfigFile(): Promise<void> {
try {
await fs.access(CONFIG_FILE);
} catch {
const template = `
FRP_HOST=""
FRP_USER=""
FRP_SECRET=""
`;
console.error(`❌ Config not found. Template created at: ${CONFIG_FILE}`);
console.log(template);
console.log(templateConfig);
process.exit(1);
}
}
@@ -66,12 +69,19 @@ async function loadConfig(): Promise<FrpConfig> {
conf[key] = value;
}
if (!conf.FRP_HOST || !conf.FRP_USER || !conf.FRP_SECRET || !conf.FRP_AUTH_TOKEN) {
console.error(`❌ Config not found. Template created at: ${CONFIG_FILE}`);
console.log(raw);
process.exit(1);
}
return {
FRP_HOST: conf.FRP_HOST || "",
FRP_PORT: "443",
FRP_USER: conf.FRP_USER || "",
FRP_SECRET: conf.FRP_SECRET || "",
FRP_PROTO: "https",
FRP_AUTH_TOKEN: conf.FRP_AUTH_TOKEN || "",
};
}