Refactor folder names
1
bitchmin-api/.virtualenv
Normal file
@@ -0,0 +1 @@
|
|||||||
|
BitchMin
|
||||||
@@ -26,6 +26,7 @@ CELERY_TASK_LIST = [
|
|||||||
]
|
]
|
||||||
import flask_monitoringdashboard as dashboard
|
import flask_monitoringdashboard as dashboard
|
||||||
|
|
||||||
|
|
||||||
def create_app(app_name='bitchmin', config_class=Config):
|
def create_app(app_name='bitchmin', config_class=Config):
|
||||||
logger.info('Creating app {}'.format(app_name))
|
logger.info('Creating app {}'.format(app_name))
|
||||||
app = Flask(app_name)
|
app = Flask(app_name)
|
||||||
@@ -64,6 +64,11 @@ def check_host_record():
|
|||||||
host = args['host']
|
host = args['host']
|
||||||
|
|
||||||
actual_ip_records = get_dns_records(host, ip, os.getenv('DNS_SERVER'))
|
actual_ip_records = get_dns_records(host, ip, os.getenv('DNS_SERVER'))
|
||||||
|
if len(actual_ip_records) == 0:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'error',
|
||||||
|
'payload': 'Host {} is in not found in {}'.format(host, os.getenv('DNS_SERVER'), ip)
|
||||||
|
}), 200
|
||||||
|
|
||||||
if ip in actual_ip_records:
|
if ip in actual_ip_records:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
@@ -13,7 +13,8 @@ class Config(object):
|
|||||||
ISDEV = ISDEV
|
ISDEV = ISDEV
|
||||||
SECRET_KEY = os.getenv('SECRET_KEY') or 'you-will-never-guess'
|
SECRET_KEY = os.getenv('SECRET_KEY') or 'you-will-never-guess'
|
||||||
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
|
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
|
||||||
'sqlite:///' + os.path.join(basedir, '../app.db')
|
'postgresql+psycopg2://bitchmin:bitchmin@localhost/bitchmin'
|
||||||
|
# 'sqlite:///' + os.path.join(basedir, '../app.db')
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
LOG_TO_STDOUT = os.getenv('LOG_TO_STDOUT')
|
LOG_TO_STDOUT = os.getenv('LOG_TO_STDOUT')
|
||||||
ADMINS = ['Ferg@lMoran.me']
|
ADMINS = ['Ferg@lMoran.me']
|
||||||
@@ -8,7 +8,7 @@ sqlalchemy_utils
|
|||||||
phue
|
phue
|
||||||
python-dotenv
|
python-dotenv
|
||||||
PyJWT
|
PyJWT
|
||||||
psycopg2-binary==2.8.5
|
psycopg2-binary==2.8.6
|
||||||
Psycopg2
|
Psycopg2
|
||||||
pylint
|
pylint
|
||||||
werkzeug
|
werkzeug
|
||||||
@@ -8,7 +8,6 @@ module.exports = {
|
|||||||
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/typescript/recommended'],
|
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/typescript/recommended'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
project: ['./tsconfig.json', './tsconfig.eslint.json'],
|
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
5
bitchmin-client/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
@@ -1,8 +1,8 @@
|
|||||||
import axios, {
|
import axios, {
|
||||||
AxiosError,
|
AxiosError,
|
||||||
AxiosInstance,
|
AxiosInstance,
|
||||||
AxiosRequestConfig,
|
AxiosRequestConfig,
|
||||||
AxiosResponse,
|
AxiosResponse,
|
||||||
} from 'axios';
|
} from 'axios';
|
||||||
import createAuthRefreshInterceptor from 'axios-auth-refresh';
|
import createAuthRefreshInterceptor from 'axios-auth-refresh';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
@@ -13,54 +13,54 @@ export class Api {
|
|||||||
api: AxiosInstance;
|
api: AxiosInstance;
|
||||||
|
|
||||||
tokenRefreshCallback = (failedRequest: any) => {
|
tokenRefreshCallback = (failedRequest: any) => {
|
||||||
this.refreshing = true;
|
this.refreshing = true;
|
||||||
return this.api
|
return this.api
|
||||||
.post(`${process.env.VUE_APP_API_SERVER}/auth/token/refresh`)
|
.post(`${process.env.VUE_APP_API_SERVER}/auth/token/refresh`)
|
||||||
.then((tokenRefreshResponse) => {
|
.then((tokenRefreshResponse) => {
|
||||||
this.refreshing = false;
|
this.refreshing = false;
|
||||||
store.dispatch;
|
store.dispatch;
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'access_token',
|
'access_token',
|
||||||
tokenRefreshResponse.data.accessToken,
|
tokenRefreshResponse.data.accessToken,
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'refresh_token',
|
'refresh_token',
|
||||||
tokenRefreshResponse.data.refreshToken,
|
tokenRefreshResponse.data.refreshToken,
|
||||||
);
|
);
|
||||||
failedRequest.response.config.headers.Authorization = `Bearer ${tokenRefreshResponse.data.accessToken}`;
|
failedRequest.response.config.headers.Authorization = `Bearer ${tokenRefreshResponse.data.accessToken}`;
|
||||||
store.dispatch('updateToken', {
|
store.dispatch('updateToken', {
|
||||||
accessToken: tokenRefreshResponse.data.accessToken,
|
accessToken: tokenRefreshResponse.data.accessToken,
|
||||||
refreshToken: tokenRefreshResponse.data.refreshToken,
|
refreshToken: tokenRefreshResponse.data.refreshToken,
|
||||||
});
|
});
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
public constructor(config?: AxiosRequestConfig) {
|
public constructor(config?: AxiosRequestConfig) {
|
||||||
this.api = axios.create(config);
|
this.api = axios.create(config);
|
||||||
this.__setupInterceptors();
|
this.__setupInterceptors();
|
||||||
|
|
||||||
this.getUri = this.getUri.bind(this);
|
this.getUri = this.getUri.bind(this);
|
||||||
this.request = this.request.bind(this);
|
this.request = this.request.bind(this);
|
||||||
this.get = this.get.bind(this);
|
this.get = this.get.bind(this);
|
||||||
this.delete = this.delete.bind(this);
|
this.delete = this.delete.bind(this);
|
||||||
this.head = this.head.bind(this);
|
this.head = this.head.bind(this);
|
||||||
this.post = this.post.bind(this);
|
this.post = this.post.bind(this);
|
||||||
this.put = this.put.bind(this);
|
this.put = this.put.bind(this);
|
||||||
this.patch = this.patch.bind(this);
|
this.patch = this.patch.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getAccessToken = () => (this.refreshing
|
_getAccessToken = () => (this.refreshing
|
||||||
? localStorage.getItem('refresh_token')
|
? localStorage.getItem('refresh_token')
|
||||||
: localStorage.getItem('access_token'));
|
: localStorage.getItem('access_token'));
|
||||||
|
|
||||||
private __setupInterceptors(): void {
|
private __setupInterceptors(): void {
|
||||||
// Use interceptor to inject the token to requests
|
// Use interceptor to inject the token to requests
|
||||||
this.api.interceptors.request.use((request) => {
|
this.api.interceptors.request.use((request) => {
|
||||||
request.headers.Authorization = `Bearer ${this._getAccessToken()}`;
|
request.headers.Authorization = `Bearer ${this._getAccessToken()}`;
|
||||||
return request;
|
return request;
|
||||||
});
|
});
|
||||||
createAuthRefreshInterceptor(this.api, this.tokenRefreshCallback);
|
createAuthRefreshInterceptor(this.api, this.tokenRefreshCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +71,7 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public getUri(config?: AxiosRequestConfig): string {
|
public getUri(config?: AxiosRequestConfig): string {
|
||||||
return this.api.getUri(config);
|
return this.api.getUri(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,9 +96,9 @@ export class Api {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public request<T, R = AxiosResponse<T>>(
|
public request<T, R = AxiosResponse<T>>(
|
||||||
config: AxiosRequestConfig,
|
config: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.request(config);
|
return this.api.request(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,10 +113,10 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public get<T, R = AxiosResponse<T>>(
|
public get<T, R = AxiosResponse<T>>(
|
||||||
url: string,
|
url: string,
|
||||||
config?: AxiosRequestConfig,
|
config?: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.get(url, config);
|
return this.api.get(url, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,10 +131,10 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public delete<T, R = AxiosResponse<T>>(
|
public delete<T, R = AxiosResponse<T>>(
|
||||||
url: string,
|
url: string,
|
||||||
config?: AxiosRequestConfig,
|
config?: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.delete(url, config);
|
return this.api.delete(url, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,10 +149,10 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public head<T, R = AxiosResponse<T>>(
|
public head<T, R = AxiosResponse<T>>(
|
||||||
url: string,
|
url: string,
|
||||||
config?: AxiosRequestConfig,
|
config?: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.head(url, config);
|
return this.api.head(url, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,11 +170,11 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public post<T, B, R = AxiosResponse<T>>(
|
public post<T, B, R = AxiosResponse<T>>(
|
||||||
url: string,
|
url: string,
|
||||||
data?: B,
|
data?: B,
|
||||||
config?: AxiosRequestConfig,
|
config?: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.post(url, data, config);
|
return this.api.post(url, data, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,11 +191,11 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public put<T, B, R = AxiosResponse<T>>(
|
public put<T, B, R = AxiosResponse<T>>(
|
||||||
url: string,
|
url: string,
|
||||||
data?: B,
|
data?: B,
|
||||||
config?: AxiosRequestConfig,
|
config?: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.put(url, data, config);
|
return this.api.put(url, data, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,11 +212,11 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public patch<T, B, R = AxiosResponse<T>>(
|
public patch<T, B, R = AxiosResponse<T>>(
|
||||||
url: string,
|
url: string,
|
||||||
data?: B,
|
data?: B,
|
||||||
config?: AxiosRequestConfig,
|
config?: AxiosRequestConfig,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
return this.api.patch(url, data, config);
|
return this.api.patch(url, data, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,10 +227,10 @@ export class Api {
|
|||||||
* @memberof Api
|
* @memberof Api
|
||||||
*/
|
*/
|
||||||
public success<T>(response: AxiosResponse<T>): T {
|
public success<T>(response: AxiosResponse<T>): T {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public error(error: AxiosError<Error>) {
|
public error(error: AxiosError<Error>) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,17 +7,17 @@ import { apiConfig } from './config';
|
|||||||
import { Api } from './apiBase';
|
import { Api } from './apiBase';
|
||||||
|
|
||||||
export class AuthApi extends Api {
|
export class AuthApi extends Api {
|
||||||
constructor(config: AxiosRequestConfig) {
|
constructor(config: AxiosRequestConfig) {
|
||||||
// NEVER FORGET THE SUPER
|
// NEVER FORGET THE SUPER
|
||||||
super(config);
|
super(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public login(user: UserLoginModel): Promise<AxiosResponse<AuthResult>> {
|
public login(user: UserLoginModel): Promise<AxiosResponse<AuthResult>> {
|
||||||
return this.post('/auth/login/', user);
|
return this.post('/auth/login/', user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public register(user: UserLoginModel) {
|
public register(user: UserLoginModel) {
|
||||||
return this.post('/auth/register/', user);
|
return this.post('/auth/register/', user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const authApi = new AuthApi(apiConfig);
|
export const authApi = new AuthApi(apiConfig);
|
||||||
20
bitchmin-client/src/api/config.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import * as qs from 'qs';
|
||||||
|
|
||||||
|
export const API_BASE = process.env.VUE_APP_API_SERVER;
|
||||||
|
|
||||||
|
export const apiConfig = {
|
||||||
|
returnRejectedPromiseOnError: true,
|
||||||
|
withCredentials: true,
|
||||||
|
credentials: 'same-origin',
|
||||||
|
timeout: 30000,
|
||||||
|
baseURL: API_BASE,
|
||||||
|
headers: {
|
||||||
|
common: {
|
||||||
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||||
|
Pragma: 'no-cache',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
paramsSerializer: (params: any) => qs.stringify(params, { indices: false }),
|
||||||
|
};
|
||||||
@@ -6,14 +6,14 @@ import { ApiResult, DataApiResult } from '@/api/apiResult';
|
|||||||
import { User } from '@/models';
|
import { User } from '@/models';
|
||||||
|
|
||||||
export class DebugApi extends Api {
|
export class DebugApi extends Api {
|
||||||
constructor(config: AxiosRequestConfig) {
|
constructor(config: AxiosRequestConfig) {
|
||||||
// NEVER FORGET THE SUPER
|
// NEVER FORGET THE SUPER
|
||||||
super(config);
|
super(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getDebug(): Promise<string> {
|
public async getDebug(): Promise<string> {
|
||||||
const result = await this.get<DataApiResult<string>>('/debug');
|
const result = await this.get<DataApiResult<string>>('/debug');
|
||||||
return result.data.payload || '';
|
return result.data.payload || '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const debugApi = new DebugApi(apiConfig);
|
export const debugApi = new DebugApi(apiConfig);
|
||||||
80
bitchmin-client/src/api/dnsApi.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { Api } from '@/api/apiBase';
|
||||||
|
import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
|
import { apiConfig } from '@/api/config';
|
||||||
|
import { ApiResult, DataApiResult } from '@/api/apiResult';
|
||||||
|
import { DnsRecord } from '@/models/dnsRecord';
|
||||||
|
|
||||||
|
export class DnsApi extends Api {
|
||||||
|
constructor(config: AxiosRequestConfig) {
|
||||||
|
// NEVER FORGET THE SUPER
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateDnsRecord(
|
||||||
|
hostName: string,
|
||||||
|
ipAddress: string,
|
||||||
|
): Promise<DataApiResult<DnsRecord>> {
|
||||||
|
const result = await this.post<
|
||||||
|
ApiResult,
|
||||||
|
any,
|
||||||
|
AxiosResponse<DataApiResult<DnsRecord>>
|
||||||
|
>('/dns/', {
|
||||||
|
host: hostName,
|
||||||
|
ip: ipAddress,
|
||||||
|
});
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteDnsRecord(
|
||||||
|
hostName: string,
|
||||||
|
): Promise<number> {
|
||||||
|
const result = await this.delete(`/dns/?host=${hostName}`);
|
||||||
|
return result.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async refreshDnsRecord(
|
||||||
|
hostName: string,
|
||||||
|
ip: string,
|
||||||
|
): Promise<DataApiResult<DnsRecord>> {
|
||||||
|
const result = await this.post<
|
||||||
|
ApiResult,
|
||||||
|
any,
|
||||||
|
AxiosResponse<DataApiResult<DnsRecord>>
|
||||||
|
>('/dns/refresh', {
|
||||||
|
host: hostName,
|
||||||
|
ip,
|
||||||
|
});
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async verifyDnsRecord(
|
||||||
|
hostName: string,
|
||||||
|
ip: string,
|
||||||
|
): Promise<DataApiResult<string>> {
|
||||||
|
const result = await this.post<
|
||||||
|
ApiResult,
|
||||||
|
any,
|
||||||
|
AxiosResponse<DataApiResult<string>>
|
||||||
|
>('/dns/check/', {
|
||||||
|
host: hostName,
|
||||||
|
ip,
|
||||||
|
});
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getDnsRecords(): Promise<DnsRecord[]> {
|
||||||
|
const result = await this.get<DnsRecord[]>('/dns/list');
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMyIP(): Promise<string> {
|
||||||
|
const result = await this.get<DataApiResult<string>>('/dns/myip');
|
||||||
|
return result.data.payload || 'Unknown IP';
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getHeaders(): Promise<any> {
|
||||||
|
const result = await this.get<DataApiResult<any>>('/dns/headers');
|
||||||
|
return result.data.payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const dnsApi = new DnsApi(apiConfig);
|
||||||
@@ -5,5 +5,5 @@ import { lightsApi } from './lightsApi';
|
|||||||
import { debugApi } from './debugApi';
|
import { debugApi } from './debugApi';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authApi, userApi, dnsApi, lightsApi, debugApi,
|
authApi, userApi, dnsApi, lightsApi, debugApi,
|
||||||
};
|
};
|
||||||
51
bitchmin-client/src/api/lightsApi.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
|
||||||
|
import { Api } from '@/api/apiBase';
|
||||||
|
|
||||||
|
import { apiConfig } from '@/api/config';
|
||||||
|
import { Light } from '@/models';
|
||||||
|
import { ApiResult, DataApiResult } from './apiResult';
|
||||||
|
|
||||||
|
export class LightsApi extends Api {
|
||||||
|
constructor(config: AxiosRequestConfig) {
|
||||||
|
// NEVER FORGET THE SUPER
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLights(): Promise<Light[]> {
|
||||||
|
const lights = await this.get<DataApiResult<Light[]>>(
|
||||||
|
'/lights/getlights',
|
||||||
|
);
|
||||||
|
return lights.data.payload || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeBrightness(
|
||||||
|
lightId: number,
|
||||||
|
brightness: number,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const result = await this.post<
|
||||||
|
ApiResult,
|
||||||
|
any,
|
||||||
|
AxiosResponse<ApiResult>
|
||||||
|
>('/lights/setbrightness', {
|
||||||
|
lightId,
|
||||||
|
brightness,
|
||||||
|
});
|
||||||
|
return result.data.status === 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeColour(
|
||||||
|
lightId: number,
|
||||||
|
colour: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const result = await this.post<
|
||||||
|
ApiResult,
|
||||||
|
any,
|
||||||
|
AxiosResponse<ApiResult>
|
||||||
|
>('/lights/changecolour', {
|
||||||
|
lightId,
|
||||||
|
rgbColour: colour,
|
||||||
|
});
|
||||||
|
return result.data.status === 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const lightsApi = new LightsApi(apiConfig);
|
||||||
@@ -6,14 +6,14 @@ import { ApiResult, DataApiResult } from '@/api/apiResult';
|
|||||||
import { User } from '@/models';
|
import { User } from '@/models';
|
||||||
|
|
||||||
export class UserApi extends Api {
|
export class UserApi extends Api {
|
||||||
constructor(config: AxiosRequestConfig) {
|
constructor(config: AxiosRequestConfig) {
|
||||||
// NEVER FORGET THE SUPER
|
// NEVER FORGET THE SUPER
|
||||||
super(config);
|
super(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUser(): Promise<User> {
|
public async getUser(): Promise<User> {
|
||||||
const result = await this.get<DataApiResult<User>>('/user');
|
const result = await this.get<DataApiResult<User>>('/user');
|
||||||
return result.data.payload || { fullName: '' };
|
return result.data.payload || { fullName: '' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const userApi = new UserApi(apiConfig);
|
export const userApi = new UserApi(apiConfig);
|
||||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 539 B After Width: | Height: | Size: 539 B |
@@ -58,6 +58,7 @@ export default class DnsUpdateForm extends Vue {
|
|||||||
ipAddressRules = [
|
ipAddressRules = [
|
||||||
(v: string) => !!v || 'IP address is required',
|
(v: string) => !!v || 'IP address is required',
|
||||||
(v: string) =>
|
(v: string) =>
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
|
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
|
||||||
v
|
v
|
||||||
) || 'Invalid IP Address',
|
) || 'Invalid IP Address',
|
||||||
@@ -95,59 +95,59 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'HelloWorld',
|
name: 'HelloWorld',
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
ecosystem: [
|
ecosystem: [
|
||||||
{
|
{
|
||||||
text: 'vuetify-loader',
|
text: 'vuetify-loader',
|
||||||
href: 'https://github.com/vuetifyjs/vuetify-loader',
|
href: 'https://github.com/vuetifyjs/vuetify-loader',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'github',
|
text: 'github',
|
||||||
href: 'https://github.com/vuetifyjs/vuetify',
|
href: 'https://github.com/vuetifyjs/vuetify',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'awesome-vuetify',
|
text: 'awesome-vuetify',
|
||||||
href: 'https://github.com/vuetifyjs/awesome-vuetify',
|
href: 'https://github.com/vuetifyjs/awesome-vuetify',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
importantLinks: [
|
importantLinks: [
|
||||||
{
|
{
|
||||||
text: 'Documentation',
|
text: 'Documentation',
|
||||||
href: 'https://vuetifyjs.com',
|
href: 'https://vuetifyjs.com',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Chat',
|
text: 'Chat',
|
||||||
href: 'https://community.vuetifyjs.com',
|
href: 'https://community.vuetifyjs.com',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Made with Vuetify',
|
text: 'Made with Vuetify',
|
||||||
href: 'https://madewithvuejs.com/vuetify',
|
href: 'https://madewithvuejs.com/vuetify',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Twitter',
|
text: 'Twitter',
|
||||||
href: 'https://twitter.com/vuetifyjs',
|
href: 'https://twitter.com/vuetifyjs',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Articles',
|
text: 'Articles',
|
||||||
href: 'https://medium.com/vuetify',
|
href: 'https://medium.com/vuetify',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
whatsNext: [
|
whatsNext: [
|
||||||
{
|
{
|
||||||
text: 'Explore components',
|
text: 'Explore components',
|
||||||
href: 'https://vuetifyjs.com/components/api-explorer',
|
href: 'https://vuetifyjs.com/components/api-explorer',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Select a layout',
|
text: 'Select a layout',
|
||||||
href: 'https://vuetifyjs.com/getting-started/pre-made-layouts',
|
href: 'https://vuetifyjs.com/getting-started/pre-made-layouts',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Frequently Asked Questions',
|
text: 'Frequently Asked Questions',
|
||||||
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions',
|
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
47
bitchmin-client/src/components/Media/VideoTest.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div id="container">
|
||||||
|
<video id="video" width="640" height="480" autoplay></video>
|
||||||
|
<button id="snap"></button>
|
||||||
|
<canvas id="canvas" width="640" height="480"></canvas>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {},
|
||||||
|
})
|
||||||
|
export default class VideoTest extends Vue {
|
||||||
|
async mounted() {
|
||||||
|
console.log('VideoTest', 'mounted');
|
||||||
|
const video = document.getElementById('video') as HTMLVideoElement;
|
||||||
|
if (video !== null) {
|
||||||
|
if (navigator.mediaDevices.getUserMedia) {
|
||||||
|
navigator.mediaDevices
|
||||||
|
.getUserMedia({ video: true })
|
||||||
|
.then(function (stream) {
|
||||||
|
video.srcObject = stream;
|
||||||
|
})
|
||||||
|
.catch(function (err0r) {
|
||||||
|
console.log('Something went wrong!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped=true>
|
||||||
|
#container {
|
||||||
|
margin: 0px auto;
|
||||||
|
width: 500px;
|
||||||
|
height: 375px;
|
||||||
|
border: 10px #333 solid;
|
||||||
|
}
|
||||||
|
#videoElement {
|
||||||
|
width: 500px;
|
||||||
|
height: 375px;
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
32
bitchmin-client/src/registerServiceWorker.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
import { register } from 'register-service-worker';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||||
|
ready() {
|
||||||
|
console.log(
|
||||||
|
'App is being served from cache by a service worker.\n'
|
||||||
|
+ 'For more details, visit https://goo.gl/AFskqB',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
registered() {
|
||||||
|
console.log('Service worker has been registered.');
|
||||||
|
},
|
||||||
|
cached() {
|
||||||
|
console.log('Content has been cached for offline use.');
|
||||||
|
},
|
||||||
|
updatefound() {
|
||||||
|
console.log('New content is downloading.');
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
console.log('New content is available; please refresh.');
|
||||||
|
},
|
||||||
|
offline() {
|
||||||
|
console.log('No internet connection found. App is running in offline mode.');
|
||||||
|
},
|
||||||
|
error(error) {
|
||||||
|
console.error('Error during service worker registration:', error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
107
bitchmin-client/src/router/index.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import VueRouter, { RouteConfig } from 'vue-router';
|
||||||
|
import store from '@/store';
|
||||||
|
import Home from '../views/Home.vue';
|
||||||
|
import Login from '../views/Login.vue';
|
||||||
|
import Lights from '../views/Lights.vue';
|
||||||
|
import About from '../views/About.vue';
|
||||||
|
import Debug from '../views/Debug.vue';
|
||||||
|
import BitchNS from '../views/BitchNS.vue';
|
||||||
|
import JwtDecoder from '../views/JwtDecoder.vue';
|
||||||
|
import MyIp from '../views/MyIp.vue';
|
||||||
|
import Media from '../views/Media.vue';
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
const routes: Array<RouteConfig> = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Home',
|
||||||
|
component: Home,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'Login',
|
||||||
|
component: Login,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/media',
|
||||||
|
name: 'Media',
|
||||||
|
component: Media,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/bitchns',
|
||||||
|
name: 'BitchNS',
|
||||||
|
component: BitchNS,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/jwt',
|
||||||
|
name: 'JwtDecoder',
|
||||||
|
component: JwtDecoder,
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/myip',
|
||||||
|
name: 'MyIP',
|
||||||
|
component: MyIp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/debug',
|
||||||
|
name: 'Debug',
|
||||||
|
component: Debug,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/lights',
|
||||||
|
name: 'Lights',
|
||||||
|
component: Lights,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/about',
|
||||||
|
name: 'About',
|
||||||
|
component: About,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
// abstract: true,
|
||||||
|
mode: 'history',
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (to.name === 'Home') {
|
||||||
|
console.log('index', 'Home Route Called', store.getters.isLoggedIn);
|
||||||
|
}
|
||||||
|
if (to.matched.some((record) => record.meta.requiresAuth)) {
|
||||||
|
if (store.getters.isLoggedIn) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next('/login');
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||