Skip to content
This repository was archived by the owner on Apr 27, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cli/deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export { Command } from "https://deno.land/x/cliffy@v0.25.1/command/mod.ts";
export {
Confirm,
Input,
Secret,
Select,
} from "https://deno.land/x/cliffy@v0.25.1/prompt/mod.ts";
export type { SelectOptionSettings } from "https://deno.land/x/cliffy@v0.25.1/prompt/select.ts";
export { colors } from "https://deno.land/x/cliffy@v0.25.0/ansi/colors.ts";
19 changes: 19 additions & 0 deletions cli/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IAuthResponse } from "./types/api.ts";
import { Auth } from "./ui/auth.ts";
import { MainMenu } from "./ui/main-menu.ts";
import { clear } from "./ui/utils.ts";

export let jwt = "";
export let user = {};

async function main() {
clear();

const data = await Auth() as IAuthResponse;
jwt = data.jwt;
user = data.user;

await MainMenu.show();
}

await main();
28 changes: 28 additions & 0 deletions cli/types/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export interface IAuthPayload {
email: string;
password: string;
}

export interface IAuthResponse {
jwt: string;
user: IUser;
}

export interface IUser {
id: number;
}

export interface IGroup {
id?: number;
name?: string;
owner?: number;
}

export interface IUserGroup {
id?: number;
confirmed?: boolean;
createdAt?: string;
updatedAt?: string;
blocked?: boolean;
group?: IGroup;
}
61 changes: 61 additions & 0 deletions cli/ui/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { colors, Input, Secret } from "../deps.ts";
import { clear } from "./utils.ts";
import { IAuthPayload, IAuthResponse } from "../types/api.ts";

let email: string,
password: string;

email = "pronocup1@yopmail.com";
password = "Valentin74!";

async function authenticate({ email, password }: IAuthPayload) {
const res = await fetch("http://localhost:1337/api/auth/local", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ identifier: email, password }),
});

const resJson = await res.json();

if (resJson.error) {
const err = resJson.error;
return {
data: null,
error: colors.bold.red(`${err.name} (${err.status}): ${err.message}`),
};
}

return {
data: { jwt: resJson.jwt as string, user: resJson.user } as IAuthResponse,
error: false,
};
}

export async function Auth(
errorMessage?: string,
): Promise<IAuthResponse | void> {
clear(errorMessage);

const emailInput = Input;
emailInput.inject(email);
email = await emailInput.prompt({
message: "📧 Enter your email",
minLength: 1,
});

password = password ?? await Secret.prompt({
message: "🔒 Enter you password",
minLength: 1,
});

const { data, error } = await authenticate({ email, password });

if (error && typeof error === "string") {
password = "";
return await Auth(error);
}

return data as IAuthResponse;
}
124 changes: 124 additions & 0 deletions cli/ui/class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { Select, SelectOptionSettings } from "../deps.ts";
import { jwt } from "../mod.ts";

interface ICliViewOptions {
title?: string;
parent?: CliView;
prefetch?: IPrefetch;
options: (arg0: IOptionsOptions) => Promise<SelectOptionSettings[] | void>;
handleValue?: (value: string) => Promise<void>;
}

type CliViewParams = Record<string, unknown>;

interface IOptionsOptions {
params?: CliViewParams;
data: Record<string, unknown>;
}

interface IPrefetch {
url: string | ((params: CliViewParams) => string);
method?: string;
body?: Record<string, unknown>;
}

export class CliView {
title;
parent;
prefetch;
options;
handleValue;
params = {};

constructor(options: ICliViewOptions) {
this.title = options.title;
this.parent = options?.parent;
this.prefetch = options?.prefetch;
this.options = options.options;
this.handleValue = options?.handleValue;
}

async show(params?: CliViewParams) {
this.params = params ?? {};

this.clear();

const rawResponse = await this.fetchData(params);

const options = await this.options({ params, data: rawResponse });

const value = await Select.prompt({
message: this.getTitle(),
options: [
...(options || []),
...(
this.parent
? [
Select.separator("——————————"),
{ name: "⬅️ Go back", value: "back" },
]
: []
),
],
});

if (value === "back") {
if (this.parent) {
await this.parent?.show(this.parent?.params);
}
}

if (typeof this.handleValue === "function") {
await this.handleValue(value);
}
}

async fetchData(params?: CliViewParams) {
let rawResponse;

if (this.prefetch) {
const url = typeof this.prefetch.url === "function"
? this.prefetch.url({ ...params })
: this.prefetch.url;

const res = await fetch(url, {
method: this.prefetch.method ?? "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${jwt}`,
},
body: typeof this.prefetch.body === "object"
? JSON.stringify({ ...this.prefetch.body })
: undefined,
});

rawResponse = await res.json();
}

return rawResponse;
}

getTitle() {
let title = "";

if (this.parent) {
title += this.parent.getTitle() + " 〉";
}

return title + this.title;
}

clear(messages?: string | string[]) {
console.log("\x1Bc");

if (messages) {
if (typeof messages === "string") {
messages = [messages];
}

messages.forEach((msg) => {
console.error(msg);
});
}
}
}
79 changes: 79 additions & 0 deletions cli/ui/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Confirm, Input, Select, SelectOptionSettings } from "../deps.ts";
import { IGroup, IUserGroup } from "../types/api.ts";
import { clear } from "./utils.ts";
import { jwt } from "../mod.ts";
import { CliView } from "./class.ts";

export const GroupMain = new CliView({
title: "Groups",
prefetch: {
url: "http://localhost:1337/api/user-groups",
},
options: async ({ data }) =>
await [
// @ts-ignore Data comming from api
...data.data.map((ug: IUserGroup) => ({
name: `- ${ug?.group?.name}`,
value: `${ug?.group?.id}`,
})),
Select.separator("——————————"),
{ name: "🔍 Join a Group", value: "search", disabled: true },
{ name: "➕ Create new Group", value: "new" },
],
handleValue: async (value) => {
if (typeof +value === "number" && !isNaN(+value)) {
await GroupDetails.show({ groupId: +value });
}
},
});

export const GroupDetails = new CliView({
title: "Users",
parent: GroupMain,
prefetch: {
url: (params) => {
return typeof params?.groupId === "number"
? `http://localhost:1337/api/groups/${+params.groupId}`
: "";
},
},
options: async ({ data }) => {
// @ts-ignore Data comming from api
const allUserGroups = data?.attributes["user-groups"];
if (!Array.isArray(allUserGroups)) return await GroupMain.show();
return allUserGroups.map((ug) => ({
name: ug?.user?.username,
value: "" + ug?.user?.id,
} as SelectOptionSettings));
},
});

export async function GroupForm(_group?: IGroup) {
clear();

const name = await Input.prompt({
message: "Enter group name",
minLength: 1,
});

const confirmed = await Confirm.prompt(
`Are you sure you want to create group ${name}?`,
);

if (confirmed) {
const res = await fetch("http://localhost:1337/api/groups", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${jwt}`,
},
body: JSON.stringify({ data: { name } }),
});

const resJson = await res.json();

if (typeof resJson?.data?.id === "number") {
await GroupMain.show();
}
}
}
15 changes: 15 additions & 0 deletions cli/ui/main-menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CliView } from "./class.ts";
import { GroupMain } from "./group.ts";

export const MainMenu = new CliView({
title: "Pronocup",
options: async () =>
await [
{ name: "👥 Groups", value: "group", disabled: false },
{ name: "⚽ Predictions", value: "group", disabled: false },
],
handleValue: async (value) => {
const views = { group: GroupMain } as Record<string, CliView>;
if (Object.keys(views).includes(value)) return await views[value].show();
},
});
13 changes: 13 additions & 0 deletions cli/ui/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export function clear(messages?: string | string[]) {
console.log("\x1Bc");

if (messages) {
if (typeof messages === "string") {
messages = [messages];
}

messages.forEach((msg) => {
console.error(msg);
});
}
}