├── log.txt
├── .eslintrc.json
├── public
├── favicon.ico
└── replit.svg
├── postcss.config.js
├── styles
└── globals.css
├── next-env.d.ts
├── pages
├── _app.tsx
├── api
│ ├── hello.ts
│ ├── checkHost.ts
│ └── sendMessage.ts
└── index.tsx
├── next.config.js
├── tailwind.config.js
├── components
├── Header.tsx
├── Console.tsx
└── Auth.tsx
├── .gitignore
├── tsconfig.json
├── package.json
├── README.md
└── utils
└── autocomplete.json
/log.txt:
--------------------------------------------------------------------------------
1 | 221
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kardespro/controlmc/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | body{
5 | @apply bg-[#281640]
6 | }
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 | import type { AppProps } from 'next/app'
3 |
4 | function MyApp({ Component, pageProps }: AppProps) {
5 | return
6 | }
7 |
8 | export default MyApp
9 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | generateBuildId: async () => {
5 | return 'controlmc'
6 | },
7 | }
8 |
9 | module.exports = nextConfig
10 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./app/**/*.{js,ts,jsx,tsx}",
5 | "./pages/**/*.{js,ts,jsx,tsx}",
6 | "./components/**/*.{js,ts,jsx,tsx}",
7 | ],
8 | theme: {
9 | extend: {},
10 | },
11 | plugins: [],
12 | }
--------------------------------------------------------------------------------
/components/Header.tsx:
--------------------------------------------------------------------------------
1 | export default function Header() {
2 | return (
3 | <>
4 |
5 |
6 |
ControlMC
7 |
8 |
9 | >
10 | )
11 | }
--------------------------------------------------------------------------------
/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from 'next'
3 |
4 | type Data = {
5 | name: string
6 | }
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse
11 | ) {
12 | res.status(200).json({ name: 'John Doe' })
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/components/Console.tsx:
--------------------------------------------------------------------------------
1 | interface ConsoleProps {
2 | command: string;
3 | message: string;
4 | }
5 |
6 | const Console: React.FC = ({ command, message }) => {
7 | return (
8 | <>
9 |
10 |
11 |
root@controlmc : {command}
12 |
13 |
14 |
15 | [Server] {message}
16 |
17 |
18 |
19 | >
20 | );
21 | };
22 |
23 | export default Console;
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "axios": "^1.4.0",
13 | "next": "12.2.0",
14 | "rcon-client": "^4.2.3",
15 | "react": "18.2.0",
16 | "react-dom": "18.2.0"
17 | },
18 | "devDependencies": {
19 | "@types/node": "18.0.1",
20 | "@types/react": "18.0.14",
21 | "@types/react-dom": "18.0.5",
22 | "autoprefixer": "^10.4.13",
23 | "eslint": "8.19.0",
24 | "eslint-config-next": "12.2.0",
25 | "postcss": "^8.4.19",
26 | "tailwindcss": "^3.2.4",
27 | "typescript": "4.7.4"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/components/Auth.tsx:
--------------------------------------------------------------------------------
1 | export default function Authenication(){
2 | return(
3 | <>
4 |
5 |
6 |
7 |
8 |
13 |
Authenicating...
14 |
15 |
16 |
17 |
18 |
19 | >
20 | )
21 | }
--------------------------------------------------------------------------------
/pages/api/checkHost.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import { Rcon } from 'rcon-client';
3 |
4 | interface QueryParams {
5 | host: string;
6 | port: number;
7 | password: string;
8 | }
9 |
10 | interface ResponseData {
11 | status: number;
12 | message: string;
13 | }
14 |
15 | export default async function handler(
16 | req: NextApiRequest,
17 | res: NextApiResponse
18 | ) {
19 | const { host, port, password } = req.query;
20 | if (typeof host !== 'string' || typeof port !== 'string' || typeof password !== 'string') {
21 | return res.status(400).json({ status: 400, message: 'Missing or invalid parameters' });
22 | }
23 |
24 | const parsedPort = parseInt(port, 10);
25 | if (isNaN(parsedPort)) {
26 | return res.status(400).json({ status: 400, message: 'Invalid port number' });
27 | }
28 |
29 | const queryParams: QueryParams = {
30 | host,
31 | port: parsedPort,
32 | password,
33 | };
34 |
35 | const rcon = new Rcon({
36 | host: queryParams.host,
37 | port: queryParams.port,
38 | password: queryParams.password,
39 | });
40 |
41 | try {
42 | await rcon.connect();
43 | res.status(200).json({ status: 200, message: 'Connected' });
44 | } catch (error: any) {
45 | res.status(500).json({ status: 500, message: 'An error occurred while connecting' });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/public/replit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/pages/api/sendMessage.ts:
--------------------------------------------------------------------------------
1 | import { NextApiRequest, NextApiResponse } from 'next';
2 | import { Rcon } from 'rcon-client';
3 |
4 | interface QueryParams {
5 | host: string;
6 | port: number;
7 | password: string;
8 | message: string;
9 | }
10 |
11 | interface ResponseData {
12 | status: number;
13 | message: string;
14 | }
15 |
16 | export default async function handler(
17 | req: NextApiRequest,
18 | res: NextApiResponse
19 | ) {
20 | const { host, port, password, message } = req.query;
21 | if (!host || !port || !password || !message) {
22 | return res.status(400).json({ status: 400, message: 'Missing parameters' });
23 | }
24 |
25 | const parsedPort = parseInt(port as string, 10);
26 | if (isNaN(parsedPort)) {
27 | return res.status(400).json({ status: 400, message: 'Invalid port number' });
28 | }
29 |
30 | const queryParams: QueryParams = {
31 | host: host as string,
32 | port: parsedPort,
33 | password: password as string,
34 | message: message as string,
35 | };
36 |
37 | const rcon = new Rcon({
38 | host: queryParams.host,
39 | port: queryParams.port,
40 | password: queryParams.password,
41 | });
42 |
43 | try {
44 | await rcon.connect();
45 | const response = await rcon.send(queryParams.message);
46 | res.status(200).json({ status: 200, message: response });
47 | } catch (error: any) {
48 | res.status(500).json({ status: 500, message: 'An error occurred while connecting' });
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # ControlMC - Minecraft Pocket Edition Control Panel
3 |
4 | [](https://opensource.org/licenses/MIT)
5 |
6 | ControlMC is a web-based control panel for managing Minecraft Pocket Edition servers using the RCON (Remote Console) protocol. It allows you to perform various server management tasks through an easy-to-use web interface.
7 |
8 | ## Features
9 |
10 | - Connect to your Minecraft Pocket Edition server using RCON protocol
11 | - Execute commands directly from the control panel
12 | - Manage players, ban/unban users
13 | - View server logs and real-time console output
14 | - Easy-to-use and mobile-friendly user interface
15 |
16 | ## Built With
17 |
18 | - [Next.js](https://nextjs.org/) - The React framework for production
19 | - [TypeScript](https://www.typescriptlang.org/) - Typed JavaScript at Any Scale
20 | - [Tailwind CSS](https://tailwindcss.com/) - A utility-first CSS framework
21 | - [RCON Protocol](https://wiki.vg/RCON) - The protocol used to communicate with the Minecraft server
22 |
23 | ## Getting Started
24 |
25 | ### Prerequisites
26 |
27 | - Node.js and npm installed on your machine.
28 |
29 | ### Installation
30 |
31 | 1. Clone the repository:
32 |
33 | ```bash
34 | git clone https://github.com/kardespro/ControlMC.git
35 | cd ControlMC
36 | ```
37 |
38 | 2. Install dependencies:
39 |
40 | ```bash
41 | npm install
42 | ```
43 |
44 | ### Usage
45 |
46 | 1. Set up your Minecraft server with RCON enabled and get the RCON credentials (hostname, port, and RCON password).
47 |
48 | 2. Start the development server:
49 |
50 | ```bash
51 | npm run dev
52 | ```
53 | or
54 |
55 | ```bash
56 | npm run build && npm run start
57 | ```
58 |
59 | 4. Open your browser and go to `http://localhost:3000` to access the ControlMC control panel.
60 |
61 | ## Contributing
62 |
63 | Contributions are welcome! Feel free to open issues and submit pull requests.
64 |
65 | ## License
66 |
67 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { NextPage } from 'next';
2 | import Head from 'next/head';
3 | import Header from '../components/Header';
4 | import { useEffect, useState } from 'react';
5 | import axios from 'axios';
6 | import Auth from '../components/Auth';
7 | import Console from '../components/Console';
8 |
9 | interface FormData {
10 | host: string;
11 | password: string;
12 | port: string;
13 | }
14 |
15 | const Home: NextPage = () => {
16 | const [authenticated, setAuthenticated] = useState(false);
17 | const [loading, setLoading] = useState(false);
18 | const [output, setOutput] = useState<{ command: string; message: string }>({ command: '', message: '' });
19 |
20 | useEffect(() => {
21 | (async () => {
22 | let w = window.localStorage.getItem('creds');
23 | if (w === null) {
24 | w = ''; // Convert null to an empty string
25 | }
26 | let parsed: FormData = JSON.parse(w);
27 | if (parsed.host && parsed.password && parsed.port) {
28 | try {
29 | let data = await axios.get(`/api/checkHost?host=${parsed.host}&password=${parsed.password}&port=${parsed.port}`);
30 | if (data.data.status === 200) {
31 | setLoading(true);
32 | setAuthenticated(true);
33 | setTimeout(() => setLoading(false), 3000);
34 | } else {
35 | setAuthenticated(false);
36 | }
37 | } catch (error) {
38 | setAuthenticated(false);
39 | }
40 | }
41 | })();
42 | }, []);
43 |
44 | const handleSubmit = async (e: React.FormEvent) => {
45 | e.preventDefault();
46 | setLoading(true);
47 |
48 | const formData: FormData = {
49 | host: e.currentTarget.host.value,
50 | password: e.currentTarget.password.value,
51 | port: e.currentTarget.port.value,
52 | };
53 |
54 | try {
55 | let data = await axios.get(`/api/checkHost?host=${formData.host}&password=${formData.password}&port=${formData.port}`);
56 | if (data.data.status === 200) {
57 | setLoading(true);
58 | setAuthenticated(true);
59 | window.localStorage.setItem('creds', JSON.stringify(formData));
60 | setTimeout(() => setLoading(false), 3000);
61 | } else {
62 | setLoading(false);
63 | setAuthenticated(false);
64 | }
65 | } catch (error) {
66 | setLoading(false);
67 | setAuthenticated(false);
68 | }
69 | };
70 |
71 | const handleSubmitCommand = async (e: React.FormEvent) => {
72 | e.preventDefault();
73 | let w = window.localStorage.getItem('creds');
74 | if (w === null) {
75 | return;
76 | }
77 | let parsed: FormData = JSON.parse(w);
78 | try {
79 | let d = await axios.get(`/api/sendMessage?host=${parsed.host}&port=${parsed.port}&password=${parsed.password}&message=${e.currentTarget.command.value}`);
80 | setOutput({
81 | command: e.currentTarget.command.value,
82 | message: d.data.message,
83 | });
84 | } catch (err) {
85 | console.log('Invalid Rcon Session or Expired');
86 | setOutput({
87 | command: 'try again',
88 | message: 'Invalid Rcon Session or Expired',
89 | });
90 | }
91 | };
92 |
93 | return (
94 | <>
95 |
96 | ControlMC
97 |
98 |
99 |
100 |
101 |
102 | {authenticated ? (
103 | <>
104 | {loading ? (
105 |
106 | ) : (
107 | <>
108 |
109 |
110 |
111 | {output.command !== '' ? (
112 | <>
113 |
114 | >
115 | ) : (
116 | <>
117 |
118 | >
119 | )}
120 |
131 |
132 |
133 |
134 | >
135 | )}
136 | >
137 | ) : (
138 | <>
139 | {loading ? (
140 |
141 | ) : (
142 | <>
143 |
183 | >
184 | )}
185 | >
186 | )}
187 |
188 | >
189 | );
190 | };
191 |
192 | export default Home;
193 |
--------------------------------------------------------------------------------
/utils/autocomplete.json:
--------------------------------------------------------------------------------
1 | [
2 | "achievement give *",
3 | "achievement give * ",
4 | "achievement give ",
5 | "achievement give ",
6 | "achievement take *",
7 | "achievement take * ",
8 | "achievement take ",
9 | "achievement take ",
10 | "ban ",
11 | "ban ",
12 | "ban-ip ",
13 | "ban-ip ",
14 | "ban-ip ",
15 | "ban-ip ",
16 | "banlist ips",
17 | "banlist players",
18 | "blockdata ",
19 | "clear",
20 | "clear ",
21 | "clear - ",
22 | "clear
- ",
23 | "clear
- ",
24 | "clear
- ",
25 | "clone ",
26 | "clone filtered force ",
27 | "clone filtered force ",
28 | "clone filtered force ",
29 | "clone filtered move ",
30 | "clone filtered move ",
31 | "clone filtered move ",
32 | "clone filtered normal ",
33 | "clone filtered normal ",
34 | "clone filtered normal ",
35 | "clone masked",
36 | "clone masked force ",
37 | "clone masked force ",
38 | "clone masked force ",
39 | "clone masked move ",
40 | "clone masked move ",
41 | "clone masked move ",
42 | "clone masked normal ",
43 | "clone masked normal ",
44 | "clone masked normal