-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
1,846 additions
and
251 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,103 @@ | ||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). | ||
# Easypanel Templates | ||
|
||
## Getting Started | ||
In this repository, you will find the templates available in Easypanel. | ||
|
||
First, run the development server: | ||
Here is how you define a template. | ||
|
||
```bash | ||
npm run dev | ||
# or | ||
yarn dev | ||
``` | ||
```ts | ||
import type { AppService } from "~templates-utils"; | ||
import { createTemplate } from "~templates-utils"; | ||
|
||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. | ||
export default createTemplate({ | ||
name: "Adminer", | ||
schema: { | ||
type: "object", | ||
required: ["projectName", "serviceName", "domain"], | ||
properties: { | ||
projectName: { | ||
type: "string", | ||
title: "Project Name", | ||
}, | ||
serviceName: { | ||
type: "string", | ||
title: "Service Name", | ||
default: "adminer", | ||
}, | ||
domain: { | ||
type: "string", | ||
title: "Domain", | ||
}, | ||
}, | ||
} as const, | ||
generate({ projectName, serviceName, domain }) { | ||
const appService: AppService = { | ||
projectName, | ||
serviceName, | ||
source: { | ||
type: "image", | ||
image: "adminer", | ||
}, | ||
proxy: { | ||
port: 8080, | ||
secure: true, | ||
}, | ||
domains: [{ name: domain }], | ||
}; | ||
|
||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. | ||
return { | ||
services: [{ type: "app", data: appService }], | ||
}; | ||
}, | ||
}); | ||
``` | ||
|
||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. | ||
## Template Properties | ||
|
||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. | ||
- `name` | ||
- `schema` is a JSON Schema. This is used to generate the form and validate the input. Important: do not remove the `as const` at the end of your schema in order to keep the type inference working. | ||
- `generate` is the core of the template. It receives the form data and returns the template schema. | ||
- `validate` (optional) is used for custom validation rules | ||
- `transformErrors` (optional) is used for custom error messages | ||
|
||
## Learn More | ||
## Defining Templates | ||
|
||
To learn more about Next.js, take a look at the following resources: | ||
1. Duplicate any template from the `/templates` directory | ||
2. Re-export the newly created template from `/templates/_list.ts` | ||
3. Run `yarn dev` to open the testing playground | ||
4. Customize your template. | ||
5. Test your template. Inside an Easypanel instance, you can create a template from JSON. Use that feature to test the output of your template. | ||
6. Send a PR. | ||
|
||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. | ||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. | ||
## Type Safety | ||
|
||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! | ||
Templates are written in Typescript. We try to infer as much as possible from the `schema`. The `createTemplate` function doesn't do anything at the runtime. It is only used to give you better type inference. | ||
|
||
## Deploy on Vercel | ||
## Custom Validation Rules | ||
|
||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. | ||
```ts | ||
{ | ||
// ... | ||
validate(formData, errors) { | ||
if (formData.pass1 !== formData.pass2) { | ||
errors.pass2.addError("Passwords don't match"); | ||
} | ||
return errors; | ||
} | ||
} | ||
``` | ||
|
||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. | ||
## Custom Error Messages | ||
|
||
```ts | ||
{ | ||
// ... | ||
transformErrors(errors) { | ||
return errors.map((error) => { | ||
if (error.name === "pattern") { | ||
error.message = "Only digits are allowed"; | ||
} | ||
return error; | ||
}); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,38 @@ | ||
import '../styles/globals.css' | ||
import type { AppProps } from 'next/app' | ||
import { ChakraProvider, extendTheme } from "@chakra-ui/react"; | ||
import type { AppProps } from "next/app"; | ||
import Head from "next/head"; | ||
|
||
const theme = extendTheme({ | ||
colors: { | ||
brand: { | ||
50: "#ecfdf5", | ||
100: "#d1fae5", | ||
200: "#a7f3d0", | ||
300: "#6ee7b7", | ||
400: "#34d399", | ||
500: "#10b981", | ||
600: "#059669", | ||
700: "#047857", | ||
800: "#065f46", | ||
900: "#064e3b", | ||
}, | ||
}, | ||
}); | ||
|
||
function MyApp({ Component, pageProps }: AppProps) { | ||
return <Component {...pageProps} /> | ||
return ( | ||
<ChakraProvider theme={theme}> | ||
<Head> | ||
<title>Easypanel Templates Playground</title> | ||
<meta | ||
name="description" | ||
content="Testing playground for Easypanel Templates." | ||
/> | ||
<link rel="icon" href="/favicon.ico" /> | ||
</Head> | ||
<Component {...pageProps} /> | ||
</ChakraProvider> | ||
); | ||
} | ||
|
||
export default MyApp | ||
export default MyApp; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,108 @@ | ||
import type { NextPage } from 'next' | ||
import Head from 'next/head' | ||
import Image from 'next/image' | ||
import styles from '../styles/Home.module.css' | ||
import { | ||
Box, | ||
Button, | ||
Flex, | ||
Select, | ||
Stack, | ||
Text, | ||
useClipboard, | ||
} from "@chakra-ui/react"; | ||
import Form from "@rjsf/chakra-ui"; | ||
import type { NextPage } from "next"; | ||
import { useEffect, useState } from "react"; | ||
import * as templates from "../templates/_list"; | ||
|
||
const Home: NextPage = () => { | ||
return ( | ||
<div className={styles.container}> | ||
<Head> | ||
<title>Create Next App</title> | ||
<meta name="description" content="Generated by create next app" /> | ||
<link rel="icon" href="/favicon.ico" /> | ||
</Head> | ||
|
||
<main className={styles.main}> | ||
<h1 className={styles.title}> | ||
Welcome to <a href="https://nextjs.org">Next.js!</a> | ||
</h1> | ||
|
||
<p className={styles.description}> | ||
Get started by editing{' '} | ||
<code className={styles.code}>pages/index.tsx</code> | ||
</p> | ||
const [selected, setSelected] = useState<keyof typeof templates | "">(""); | ||
const [formData, setFormData] = useState<any>(); | ||
const [output, setOutput] = useState<string>(""); | ||
const { hasCopied, onCopy } = useClipboard(output); | ||
const template = selected ? templates[selected] : null; | ||
|
||
<div className={styles.grid}> | ||
<a href="https://nextjs.org/docs" className={styles.card}> | ||
<h2>Documentation →</h2> | ||
<p>Find in-depth information about Next.js features and API.</p> | ||
</a> | ||
useEffect(() => { | ||
setOutput(""); | ||
}, [selected]); | ||
|
||
<a href="https://nextjs.org/learn" className={styles.card}> | ||
<h2>Learn →</h2> | ||
<p>Learn about Next.js in an interactive course with quizzes!</p> | ||
</a> | ||
|
||
<a | ||
href="https://github.com/vercel/next.js/tree/canary/examples" | ||
className={styles.card} | ||
> | ||
<h2>Examples →</h2> | ||
<p>Discover and deploy boilerplate example Next.js projects.</p> | ||
</a> | ||
|
||
<a | ||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | ||
className={styles.card} | ||
return ( | ||
<Box | ||
display={{ lg: "grid" }} | ||
gridTemplateRows="auto 1fr" | ||
gridTemplateColumns="400px 1fr" | ||
height={{ lg: "100vh" }} | ||
bg="gray.800" | ||
> | ||
<Box | ||
shadow="md" | ||
zIndex="100" | ||
gridRow="1 / 2" | ||
gridColumn="1 / 3" | ||
px="4" | ||
py="2" | ||
bg="white" | ||
> | ||
<Flex direction="row" alignItems="center"> | ||
<Text fontWeight="bold" fontSize="lg" mr="6"> | ||
Templates | ||
</Text> | ||
<Select | ||
width="64" | ||
value={selected} | ||
onChange={(e) => { | ||
setSelected(e.target.value as any); | ||
}} | ||
> | ||
<h2>Deploy →</h2> | ||
<p> | ||
Instantly deploy your Next.js site to a public URL with Vercel. | ||
</p> | ||
</a> | ||
</div> | ||
</main> | ||
|
||
<footer className={styles.footer}> | ||
<a | ||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
<option value="">Select Template</option> | ||
{Object.entries(templates).map(([key, value]) => ( | ||
<option key={key} value={key}> | ||
{value.name} | ||
</option> | ||
))} | ||
</Select> | ||
</Flex> | ||
</Box> | ||
{template && ( | ||
<Box | ||
gridRow="2 / 3" | ||
gridColumn="1 / 2" | ||
overflow="auto" | ||
p="4" | ||
bg="white" | ||
> | ||
Powered by{' '} | ||
<span className={styles.logo}> | ||
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} /> | ||
</span> | ||
</a> | ||
</footer> | ||
</div> | ||
) | ||
} | ||
<Form | ||
schema={template.schema as any} | ||
formData={formData} | ||
onChange={(event) => setFormData(event.formData)} | ||
onSubmit={({ formData }) => { | ||
try { | ||
const output = template.generate(formData); | ||
setOutput(JSON.stringify(output, null, 2)); | ||
} catch (error) { | ||
setOutput(`Error: ${(error as Error).message}`); | ||
} | ||
}} | ||
noHtml5Validate | ||
showErrorList={false} | ||
validate={template.validate} | ||
transformErrors={template.transformErrors} | ||
> | ||
<Button type="submit" colorScheme="brand"> | ||
Generate | ||
</Button> | ||
</Form> | ||
</Box> | ||
)} | ||
<Box gridRow="2 / 3" gridColumn="2 / 3" overflow="auto" p="4"> | ||
{output && ( | ||
<Stack alignItems="start"> | ||
<Button onClick={onCopy}>{hasCopied ? "Copied" : "Copy"}</Button> | ||
<Box as="pre" overflow="auto" w="100%" color="white"> | ||
{output} | ||
</Box> | ||
</Stack> | ||
)} | ||
</Box> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default Home | ||
export default Home; |
Binary file not shown.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.