diff --git a/server/my_sql/create_tables.sql b/server/my_sql/create_tables.sql
index 0aa4227..0577ae7 100644
--- a/server/my_sql/create_tables.sql
+++ b/server/my_sql/create_tables.sql
@@ -110,19 +110,22 @@ CREATE TABLE users (
user_name TEXT,
user_email TEXT,
user_type INT,
- user_api_keys JSON,
user_last_login DATETIME,
user_image: TEXT,
- user_image: TEXT,
user_restricted_access BOOLEAN,
PRIMARY KEY (id)
);
--- User Auth
-CREATE TABLE auth (
- id SERIAL PRIMARY KEY,
- auth_entry TEXT,
- auth_type TEXT,
- auth_nickname TEXT,
- auth_permissions TEXT
-);
\ No newline at end of file
+-- Api Keys
+CREATE TABLE apikey (
+ id INT NOT NULL AUTO_INCREMENT,
+ user_id INT,
+ api_key_name TEXT,
+ api_key_value TEXT,
+ api_key_last_used DATETIME,
+ api_key_permissions TEXT,
+ api_key_logs JSON,
+ PRIMARY KEY (id),
+ FOREIGN KEY (user_id) REFERENCES users(id)
+);
+
diff --git a/website/package-lock.json b/website/package-lock.json
index dfe75f9..0de1850 100644
--- a/website/package-lock.json
+++ b/website/package-lock.json
@@ -1343,9 +1343,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001489",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
- "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
+ "version": "1.0.30001562",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz",
+ "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==",
"funding": [
{
"type": "opencollective",
diff --git a/website/src/lib/api_tools.ts b/website/src/lib/api_tools.ts
index 18784e0..226f073 100644
--- a/website/src/lib/api_tools.ts
+++ b/website/src/lib/api_tools.ts
@@ -2,17 +2,32 @@ import {NextApiRequest, NextApiResponse} from "next";
import {checkPermissions, getUserPermissions, RongoaUser, UserPermissions} from "@/lib/users";
import axios, {AxiosRequestConfig, AxiosResponse} from "axios";
import {jwtVerify, SignJWT} from "jose";
+import {getFromCache, saveToCache} from "@/lib/cache";
-
-export async function checkApiPermissions(request: NextApiRequest, response: NextApiResponse, session: any, client: any, permission: string) {
+export async function checkApiPermissions(request: NextApiRequest, response: NextApiResponse, session: any, client: any, makeQuery: any, permission: string) {
let permissions : UserPermissions | null = null
+ let api_key_data = null
+
// Check if there is an API key
let {api_key} = request.query;
if(api_key){
- // TODO: Get the api key from the database and check its permissions
+ console.log("API key: " + api_key)
+
+ // Make the query
+ const query = `SELECT api_key_permissions, api_key_logs FROM apikey WHERE api_key_value = '${api_key}'`
+ const result = await makeQuery(query, client)
+
+ // Check if the API key exists
+ if(result.length == 0) return false
+
+ // Get the permissions
+ permissions = JSON.parse(result[0].api_key_permissions)
+
+ // Get the api key data
+ api_key_data = result[0]
}else{
@@ -57,6 +72,21 @@ export async function checkApiPermissions(request: NextApiRequest, response: Nex
// Get the permissions of the user
const isAllowed = checkPermissions(permissions, permissionToCheck)
console.log(permissionToCheck + ": " + isAllowed)
+
+ // If the api key was used then store the action in the log
+ if(api_key && api_key_data) {
+
+ // Parse the log
+ let log = JSON.parse(api_key_data.api_key_logs)
+
+ // Add the action to the log
+ log.push({time: new Date().toISOString(), action: "Attempt to access " + permissionToCheck + " on " + request.url + ": " + (isAllowed ? "Allowed" : "Denied")})
+
+ // Update the log
+ const query = `UPDATE apikey SET api_key_logs = '${JSON.stringify(log)}', api_key_last_used = NOW() WHERE api_key_value = '${api_key}'`
+ await makeQuery(query, client)
+ }
+
return isAllowed
}
@@ -120,4 +150,19 @@ export async function makeRequestWithToken (
console.error('Request failed:', error.message);
throw error;
}
-};
\ No newline at end of file
+};
+
+
+export async function makeCachedRequest(key: string, url: string){
+
+ let cache = getFromCache(key)
+ if(cache){
+ return cache
+ }
+ cache = await makeRequestWithToken("get",url)
+ if(!cache.data.error){
+
+ saveToCache(key, cache.data.data)
+ }
+ return cache.data.data
+}
\ No newline at end of file
diff --git a/website/src/lib/databse.ts b/website/src/lib/databse.ts
index f92eb49..59e649e 100644
--- a/website/src/lib/databse.ts
+++ b/website/src/lib/databse.ts
@@ -78,12 +78,13 @@ export class SQLDatabase {
user_image: string;
user_restricted_access: string;
-
- // Auth Table
- auth_entry: string;
- auth_type: string;
- auth_nickname: string;
- auth_permissions: string;
+ // Api Keys Table
+ user_id: string;
+ api_key_name: string;
+ api_key_value: string;
+ api_key_last_used: string;
+ api_key_permissions: string;
+ api_key_logs: string;
constructor() {
this.database = "rongoa8jwons3_rongoadb"
@@ -155,13 +156,13 @@ export class SQLDatabase {
this.user_image = "user_image";
this.user_restricted_access = "user_restricted_access";
-
- // Auth Table
- this.auth_entry = "auth_entry";
- this.auth_type = "auth_type";
- this.auth_nickname = "auth_nickname";
- this.auth_permissions = "auth_permissions";
-
+ // Api Keys Table
+ this.user_id = "user_id";
+ this.api_key_name = "api_key_name";
+ this.api_key_value = "api_key_value";
+ this.api_key_last_used = "api_key_last_used";
+ this.api_key_permissions = "api_key_permissions";
+ this.api_key_logs = "api_key_logs";
}
}
@@ -225,12 +226,6 @@ export class PostgresSQL extends SQLDatabase{
this.months_event = "event";
this.months_start_month = "start_month";
this.months_end_month = "end_month";
-
- // Auth Table
- this.auth_entry = "entry";
- this.auth_type = "type";
- this.auth_nickname = "nickname";
- this.auth_permissions = "permissions";
}
}
diff --git a/website/src/lib/plant_data.ts b/website/src/lib/plant_data.ts
index dc7a7a4..bf64657 100644
--- a/website/src/lib/plant_data.ts
+++ b/website/src/lib/plant_data.ts
@@ -795,7 +795,7 @@ export async function fetchPlant (id: number) {
console.log(e)
continue
}
- const authorsData = authorData.data.user
+ const authorsData = authorData.data.data
if(authorsData){
authors.push(authorsData as UserDatabaseDetails)
}
@@ -1111,6 +1111,9 @@ export function dateToString(date: Date | string): string
return "Invalid Date"
}
+ // Add an hour to the date because of timezone issues
+ date.setHours(date.getHours() + 1);
+
dateString = date.toISOString();
}
diff --git a/website/src/lib/users.ts b/website/src/lib/users.ts
index 19757ac..2d3fdf0 100644
--- a/website/src/lib/users.ts
+++ b/website/src/lib/users.ts
@@ -19,11 +19,8 @@ export interface UserDatabaseDetails {
user_email: string,
user_type: number,
user_last_login: string,
- user_api_keys: object,
user_image: string,
user_restricted_access: boolean,
-
-
}
export interface UserPermissions {
@@ -96,6 +93,7 @@ export interface UserPermissions {
add: boolean;
remove: boolean;
edit: boolean;
+ fetch: boolean;
};
data: {
publicAccess: boolean;
@@ -144,9 +142,8 @@ export interface UserPermissions {
}
}
-export function getUserPermissions(user: RongoaUser | null) {
-
- let permissions : UserPermissions = {
+export const getDefaultPermissions = () : UserPermissions => {
+ return {
api: {
auth: {
edit_auth: {
@@ -221,11 +218,12 @@ export function getUserPermissions(user: RongoaUser | null) {
user: {
api_keys: {
- publicAccess: true,
+ publicAccess: false,
internalAccess: true,
add: true,
remove: true,
edit: true,
+ fetch: true,
},
data: {
@@ -271,7 +269,7 @@ export function getUserPermissions(user: RongoaUser | null) {
},
},
- data:{
+ data: {
account: {
viewPrivateDetails: false,
},
@@ -281,6 +279,11 @@ export function getUserPermissions(user: RongoaUser | null) {
},
}
}
+}
+
+export function getUserPermissions(user: RongoaUser | null) {
+
+ let permissions : UserPermissions = getDefaultPermissions();
// If there is no user logged in it must be a guest
if(user == null)
@@ -291,7 +294,6 @@ export function getUserPermissions(user: RongoaUser | null) {
// Check if they are allowed to view restricted data
permissions.data.plants.viewRestrictedSections = user.database.user_restricted_access;
- console.log("User is allowed to view restricted data: " + permissions.data.plants.viewRestrictedSections);
// If they are a member allow them to use parts of the api non-internally
if(user.database.user_type >= MEMBER_USER_TYPE) {
@@ -393,4 +395,39 @@ export function checkPermissions(permissions: UserPermissions, permission: strin
}
+export function getStrings(permissions: UserPermissions) {
+
+ let strings: string[] = []
+
+ // Loop through the permissions
+ for (let key in permissions) {
+
+ if(!permissions.hasOwnProperty(key)) continue;
+
+ // @ts-ignore
+ let value = permissions[key] as any
+ // If the value is an object, then loop through it
+ if (typeof value === "object") {
+
+ // Add the sub values to the strings
+ let subStrings = getStrings(value)
+ for (let subString of subStrings) {
+ strings.push(key + ":" + subString)
+ }
+
+ } else {
+
+ // Check if the permission is true
+ if (value === true && key !== "internalAccess") {
+ if (key === "publicAccess") {
+ strings.push("access")
+ }else{
+ strings.push(key)
+ }
+
+ }
+ }
+ }
+ return strings
+}
diff --git a/website/src/pages/account/edit.tsx b/website/src/pages/account/edit.tsx
index d1f6363..0a0a93a 100644
--- a/website/src/pages/account/edit.tsx
+++ b/website/src/pages/account/edit.tsx
@@ -5,9 +5,7 @@ import Section from "@/components/section";
import Footer from "@/components/footer";
import PageHeader from "@/components/page_header";
import styles from "@/styles/pages/account/index.module.css"
-import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
-import {faPerson} from "@fortawesome/free-solid-svg-icons";
-import {signIn, useSession} from "next-auth/react";
+import {useSession} from "next-auth/react";
import {ADMIN_USER_TYPE, EDITOR_USER_TYPE, MEMBER_USER_TYPE, RongoaUser, UserDatabaseDetails} from "@/lib/users";
import {globalStyles} from "@/lib/global_css";
import {useRouter} from "next/router";
@@ -16,6 +14,7 @@ import {FileInput, SmallInput, ValidationState} from "@/components/input_section
import {dateToString} from "@/lib/plant_data";
import {Loading} from "@/components/loading";
import {makeRequestWithToken} from "@/lib/api_tools";
+import {loginSection} from "@/pages/account/index";
export default function EditAccount() {
const pageName = "Account";
@@ -122,7 +121,7 @@ export default function EditAccount() {
try {
const response = await makeRequestWithToken("get","/api/user/email?email=" + userEmail)
- if (response.data.user) {
+ if (response.data.data) {
setValidUserEmail(["error", "Email is already in use"])
return false
}
@@ -222,7 +221,6 @@ export default function EditAccount() {
user_name: userName,
user_email: userEmail,
user_image: userLocalImage ? process.env.NEXT_PUBLIC_FTP_PUBLIC_URL + "/users/" + userID + "/" + userLocalImage?.name : (session?.user as RongoaUser).database.user_image,
- user_api_keys: (session?.user as RongoaUser).database.user_api_keys,
user_type: (session?.user as RongoaUser).database.user_type,
user_last_login: (session?.user as RongoaUser).database.user_last_login,
user_restricted_access: (session?.user as RongoaUser).database.user_restricted_access
@@ -234,16 +232,6 @@ export default function EditAccount() {
await router.push("/account")
}
- const loginSection = () => {
- return (
- <>
-
-
-
- >
- )
- }
-
const editSection = () => {
return (
<>
diff --git a/website/src/pages/account/index.tsx b/website/src/pages/account/index.tsx
index 467eed4..a06f68e 100644
--- a/website/src/pages/account/index.tsx
+++ b/website/src/pages/account/index.tsx
@@ -13,7 +13,7 @@ import {
ADMIN_USER_TYPE,
checkUserPermissions,
EDITOR_USER_TYPE,
- getUserPermissions,
+ getStrings,
MEMBER_USER_TYPE,
RongoaUser,
UserDatabaseDetails
@@ -25,7 +25,8 @@ import {useRouter} from "next/router";
import Link from "next/link";
import {dateToString, getNamesInPreference, macronCodeToChar, numberDictionary, PlantData} from "@/lib/plant_data";
import {Error} from "@/components/error";
-import {makeRequestWithToken} from "@/lib/api_tools";
+import {makeCachedRequest, makeRequestWithToken} from "@/lib/api_tools";
+import {Loading} from "@/components/loading";
export default function Account() {
@@ -55,17 +56,21 @@ export function AccountPage({dataID}: AccountPageProps){
const [userPlants, setUserPlants] = React.useState("")
const [userPosts, setUserPosts] = React.useState("")
const [userPlantsData, setUserPlantsData] = React.useState([])
+ const [userPostsData, setUserPostsData] = React.useState([])
+ const [userApiKeysData, setUserApiKeysData] = React.useState([])
+ const [userApiKeysShown, setUserApiKeysShown] = React.useState([])
const [userID, setUserID] = React.useState(0)
-
// States
+ const [loading, setLoading] = React.useState(false)
+ const [loadingMessage, setLoadingMessage] = React.useState("Loading...")
+ const[error, setError] = useState("")
const [editor, setEditor] = React.useState(false)
const [myAccount, setMyAccount] = React.useState(false)
const [hidePrivate, setHidePrivate] = React.useState(true)
// Don't fetch the data again if it has already been fetched
const dataFetch = useRef("-1")
- const[error, setError] = useState("")
useEffect(() => {
@@ -75,8 +80,11 @@ export function AccountPage({dataID}: AccountPageProps){
// Check if it is to be used
if(dataID != "0"){
- if(!checkUserPermissions(session?.user as RongoaUser, "pages:account:publicAccess"))
+ if(!checkUserPermissions(session?.user as RongoaUser, "pages:account:publicAccess")){
setError("You must be logged in to view other users")
+ setLoading(false)
+ return
+ }
// Try converting the id to a number
let localId = parseInt(dataID as string)
@@ -84,6 +92,8 @@ export function AccountPage({dataID}: AccountPageProps){
// If it is not a number then there is a problem
if(isNaN(localId)){
console.log("Not a number")
+ setError("User not found")
+ setLoading(false)
return
}
@@ -115,11 +125,10 @@ export function AccountPage({dataID}: AccountPageProps){
setHidePrivate(false)
let user = session.user as RongoaUser
- if(!user) return
-
- // Log their permissions
- console.log("User permissions: ")
- console.log(getUserPermissions(user))
+ if(!user){
+ setLoading(false)
+ return
+ }
// Load the user data
loadUserData(user.database)
@@ -170,47 +179,97 @@ export function AccountPage({dataID}: AccountPageProps){
const fetchData = async (localId: number = 0) => {
+ setLoading(true)
+
console.log("Fetching data")
// If we are viewing a user then we need to get their data
if(localId != 0) {
try {
+ setLoadingMessage("Fetching user data...")
- const user = await makeRequestWithToken("get", "/api/user/data?id=" + localId)
- if(user.data.error){
+ // Get the data
+ const user = await makeCachedRequest("userData_" + localId, "/api/user/data?id=" + localId)
+ if(!user){
setError("User not found")
+ setLoading(false)
+ return
}
- loadUserData(user.data.user)
+ loadUserData(user)
// Override the editor state
setEditor(false)
} catch (e) {
console.log(e)
+ setLoading(false)
setError("User not found")
+ return
}
}
// Get the users plants
try {
- let url = "/api/user/plants"
+ setLoadingMessage("Fetching plants...")
+ // Create the url
+ let url = "/api/user/plants"
if(localId != 0) {
url += "?id=" + localId
}
- const plants = await makeRequestWithToken("get",url)
- if(!plants.data.error){
- setUserPlants(plants.data.data.length.toString())
+ // Get the data
+ let plants = await makeCachedRequest("userPlantsData_" + localId, url)
+
+ // Update the state variables
+ if(plants){
+ setUserPlants(plants.length.toString())
+ setUserPlantsData(plants)
+ }else{
+ setUserPlants("0")
+ setUserPlantsData([])
}
- setUserPlantsData(plants.data.data)
+
} catch (e) {
// User has no plants
console.log(e)
setUserPlants("0")
}
+
+ // Get the users posts
+
+ // Get the users api keys
+ try {
+
+ setLoadingMessage("Fetching API keys...")
+
+
+ // Create the url
+ let apiUrl = "/api/user/api_keys?operation=fetch"
+ if(localId != 0 && checkUserPermissions(session?.user as RongoaUser, "data:account:viewPrivateDetails")) {
+ console.log("Can view private details")
+ apiUrl += "&publicUserID=" + localId
+ }
+
+ // Get the data
+ let apikeys = await makeCachedRequest("userApiKeysData_" + localId, apiUrl)
+ if(!apikeys)
+ apikeys = []
+
+ // Update the state variables
+ setUserApiKeysData(apikeys)
+
+ } catch (e) {
+
+ // User has no plants
+ console.log(e)
+ setUserPlants("0")
+ }
+
+ console.log("Finished fetching data")
+ setLoading(false)
}
const signOutUser = async () => {
@@ -222,16 +281,6 @@ export function AccountPage({dataID}: AccountPageProps){
await signOutUser()
}
- const loginSection = () => {
- return (
- <>
-
-
-
- >
- )
- }
-
const accountSection = () => {
return (
<>
@@ -363,12 +412,13 @@ export function AccountPage({dataID}: AccountPageProps){
-
+
ID |
Name |
- Key |
+ Value |
+ Last used |
Permissions |
Divider |
View |
@@ -376,11 +426,34 @@ export function AccountPage({dataID}: AccountPageProps){
-
- No API Keys Found |
-
+ {userApiKeysData.length > 0 ? userApiKeysData.map((apiKey: any, index) => (
+
+ {apiKey.id} |
+ {apiKey.api_key_name} |
+ {apiKey.api_key_value} |
+ {dateToString(new Date(apiKey.api_key_last_used))} |
+ {getStrings(JSON.parse(apiKey.api_key_permissions)).join(", ")} |
+ Divider |
+
+
+
+
+ |
+
+ {editor &&
+
+
+
+ | }
+
+ )) :
+ No Api Keys Found |
+
}
+
+ {editor && }
+
@@ -425,6 +498,9 @@ export function AccountPage({dataID}: AccountPageProps){
+ {/* Loading Message */}
+ {loading && }
+
{/* Error Message */}
{error ?
@@ -453,4 +529,14 @@ export function AccountPage({dataID}: AccountPageProps){
>
)
+}
+
+export const loginSection = () => {
+ return (
+ <>
+
+
+
+ >
+ )
}
\ No newline at end of file
diff --git a/website/src/pages/account/keys/create.tsx b/website/src/pages/account/keys/create.tsx
new file mode 100644
index 0000000..59a118e
--- /dev/null
+++ b/website/src/pages/account/keys/create.tsx
@@ -0,0 +1,224 @@
+import HtmlHeader from "@/components/html_header";
+import Navbar from "@/components/navbar";
+import React, {useEffect} from "react";
+import Section from "@/components/section";
+import Footer from "@/components/footer";
+import PageHeader from "@/components/page_header";
+import styles from "@/styles/pages/account/index.module.css"
+import {useSession} from "next-auth/react";
+import {useRouter} from "next/router";
+import {loginSection} from "@/pages/account";
+import {SmallInput, ValidationState} from "@/components/input_sections";
+import {getDefaultPermissions, getStrings, getUserPermissions, RongoaUser, UserPermissions} from "@/lib/users";
+import {makeRequestWithToken} from "@/lib/api_tools";
+import {Loading} from "@/components/loading";
+import {Error} from "@/components/error";
+
+export default function Account() {
+
+ return(
+
+ )
+
+}
+
+interface AccountPageProps {
+ dataID: string | string[]
+}
+
+export function AccountPage({dataID}: AccountPageProps){
+ const pageName = "Account";
+
+ const { data: session } = useSession()
+
+ const router = useRouter()
+
+ // Data states
+ const [keyName, setKeyName] = React.useState("")
+ const [keyNameState, setKeyNameState] = React.useState<[ValidationState, string]>(["normal", ""])
+ const [keyPermissions, setKeyPermissions] = React.useState([])
+
+ const [loading, setLoading] = React.useState(false)
+ const [error, setError] = React.useState("")
+
+ // Check if the user is logged in
+ useEffect(() => {
+ if (session?.user) {
+ generateKeyPermissions()
+ }
+ }, [session])
+
+
+ // Generate the key permissions
+ const generateKeyPermissions = () => {
+
+ const permissions = getUserPermissions(session?.user as RongoaUser)
+ setKeyPermissions(getStrings(permissions))
+ }
+
+ const getPermissions = () => {
+
+ let permissions: UserPermissions = getDefaultPermissions();
+
+ function updatePermissions(permissions: any, keys: string[], permissionValue: boolean) {
+ let currentLevel = permissions;
+
+ for (let i = 0; i < keys.length - 1; i++) {
+ currentLevel = currentLevel[keys[i]] = currentLevel[keys[i]] || {};
+ }
+
+ currentLevel[keys[keys.length - 1]] = permissionValue;
+ }
+
+ // Loop through the permissions
+ for (let permission of keyPermissions) {
+
+ // Get the value of the permission
+ let permissionValue = (document.getElementById(permission) as HTMLInputElement)?.checked
+
+ // Check if the permission is undefined or null
+ if (permissionValue === undefined || permissionValue === null) continue;
+
+ // Get the keys
+ let keys = permission.split(":")
+
+ // Replace access with publicAccess
+ for (let i = 0; i < keys.length; i++) {
+ if (keys[i] === "access") keys[i] = "publicAccess"
+ }
+
+ // Update the permissions
+ updatePermissions(permissions, keys, permissionValue)
+
+ }
+
+ console.log(permissions)
+ return permissions
+
+ }
+
+ const uploadKey = async () => {
+
+ setLoading(true)
+
+ // Check if the key name is valid
+ if (keyName === "") {
+ setKeyNameState(["error", "Key name cannot be empty"])
+ setLoading(false)
+ return
+ }else {
+ setKeyNameState(["success", ""])
+ }
+
+ // Get the permissions
+ const permissions = getPermissions()
+
+ // Check if there is atleast one permission set to true
+ let hasPermission = false
+ for (let permission of keyPermissions) {
+
+ // Get the value of the permission
+ let permissionValue = (document.getElementById(permission) as HTMLInputElement)?.checked
+
+ if(permissionValue) hasPermission = true
+ }
+
+ if(!hasPermission){
+ setError("You must have atleast one permission set to true")
+ setLoading(false)
+ return
+ }
+
+ // Upload the key
+ try{
+ const response = await makeRequestWithToken("post", "/api/user/api_keys?operation=new&keyName=" + keyName + "&permissions=" + JSON.stringify(permissions))
+ } catch (e) {
+ console.log(e)
+ setError((e as Error).message)
+ }
+
+ // Delete the cached user keys
+ localStorage.removeItem("userApiKeysData_0")
+
+ setLoading(false)
+ // Go to the account page
+ router.push("/account/")
+
+ }
+
+ const keysCreator = () => {
+ return (
+ <>
+
+
+
+
+
+
Key Name
+
+
+
+
+
Key Permissions
+
+ {keyPermissions.map((permission, index) => {
+ return (
+
+ {/* Check box for the permission */}
+
+
+
+ )
+ })}
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+ }
+
+
+ return(
+ <>
+
+ {/* Set up the page header and navbar */}
+
+
+
+
+ {/* Header for the page */}
+
+
+
+
Create New Key
+
+
+
+
+ {/* Loading Message */}
+ {loading && }
+
+ {/* Error Message */}
+ {error && }
+
+ {!session?
+ loginSection()
+ :
+ keysCreator()
+ }
+
+ {/* Footer */}
+
+ >
+
+ )
+}
\ No newline at end of file
diff --git a/website/src/pages/api/auth/edit_auth.ts b/website/src/pages/api/auth/edit_auth.ts
deleted file mode 100644
index 2fcefd6..0000000
--- a/website/src/pages/api/auth/edit_auth.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import {NextApiRequest, NextApiResponse} from 'next';
-import {getClient, getTables, makeQuery} from "@/lib/databse";
-
-export default async function handler(
- request: NextApiRequest,
- response: NextApiResponse,
-) {
-
-
- return response.status(200).json({ data: "Under Development" });
-
-
-
- // Get the client
- const client = await getClient()
-
- // Get the tables
- const tables = getTables();
-
- // Try uploading the data to the database
- try {
-
-
- // Get the data from the request
- let {
- operation,
- entry,
- type,
- nickname,
- permissions
- } = request.query;
-
- console.log(operation)
-
- if(!operation){
- return response.status(400).json({ error: 'Missing operation'});
- }
-
- // Check if the entry is valid
- if(operation != "fetch")
- if(!entry || !type || !nickname || !permissions){
- return response.status(400).json({ error: 'Missing entry, type, nickname or permissions', entry: entry, type: type, nickname: nickname, permissions: permissions});
- }
-
- // Convert entry to string
- if(entry)
- entry = Buffer.from(entry as string, 'base64').toString('ascii');
-
- let query = "";
- switch (operation) {
- case "add":
- // Make the query
- query = `INSERT INTO auth (${tables.auth_entry}, ${tables.auth_type}, ${tables.auth_nickname}, ${tables.auth_permissions}) VALUES ('${entry}', '${type}', '${nickname}', '${permissions}')`;
- const new_auths = await makeQuery(query, client)
- return response.status(200).json({ data: new_auths });
-
- case "remove":
- // Make the query
- query = `DELETE FROM auth WHERE ${tables.auth_entry} = '${entry}' AND ${tables.auth_type} = '${type}' AND ${tables.auth_nickname} = '${nickname}' AND ${tables.auth_permissions} = '${permissions}'`;
- console.log(query)
- const remove_auths = await makeQuery(query, client)
- return response.status(200).json({ data: remove_auths });
-
- case "fetch":
- // Make the query
- query = `SELECT * FROM auth`;
-
- // Get the auth entries
- const auths = await makeQuery(query, client)
-
- // If there are no auths, return an error
- if (!auths) {
- return response.status(404).json({ error: 'No auths found' });
- }
-
- // Return the plant ids
- return response.status(200).json({ data: auths });
- }
-
-
- } catch (error) {
- console.log("Error");
- console.log(error);
-
- // If there is an error, return the error
- return response.status(500).json({ error: error });
- }
-}
\ No newline at end of file
diff --git a/website/src/pages/api/files/backup_database.ts b/website/src/pages/api/files/backup_database.ts
index 2ce116f..07861d0 100644
--- a/website/src/pages/api/files/backup_database.ts
+++ b/website/src/pages/api/files/backup_database.ts
@@ -23,7 +23,7 @@ export default async function handler(
// Check if the user has the correct permissions
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:files:backup_database:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:files:backup_database:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Create the query
diff --git a/website/src/pages/api/files/backup_files.ts b/website/src/pages/api/files/backup_files.ts
index 9f4d7c3..020ad8a 100644
--- a/website/src/pages/api/files/backup_files.ts
+++ b/website/src/pages/api/files/backup_files.ts
@@ -1,5 +1,5 @@
import {NextApiRequest, NextApiResponse} from 'next';
-import {getClient} from "@/lib/databse";
+import {getClient, makeQuery} from "@/lib/databse";
import {checkApiPermissions} from "@/lib/api_tools";
import archiver from 'archiver';
import fs from 'fs';
@@ -104,7 +104,7 @@ export default async function handler(
// Check if the user has the correct permissions
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:files:backup_files:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:files:backup_files:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try uploading the data to the database
diff --git a/website/src/pages/api/files/upload.ts b/website/src/pages/api/files/upload.ts
index 4b58a4c..2383bdf 100644
--- a/website/src/pages/api/files/upload.ts
+++ b/website/src/pages/api/files/upload.ts
@@ -1,5 +1,5 @@
import {NextApiRequest, NextApiResponse} from 'next';
-import {getClient, PostgresSQL, SQLDatabase} from "@/lib/databse";
+import {getClient, makeQuery, PostgresSQL, SQLDatabase} from "@/lib/databse";
import {USE_POSTGRES} from "@/lib/constants";
import {Form} from "multiparty";
import fs from "fs";
@@ -23,7 +23,7 @@ export default async function handler(
// Get the client
const client = await getClient()
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:files:upload:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:files:upload:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try uploading the data to the database
diff --git a/website/src/pages/api/plants/download.ts b/website/src/pages/api/plants/download.ts
index 9f57859..1bd70bd 100644
--- a/website/src/pages/api/plants/download.ts
+++ b/website/src/pages/api/plants/download.ts
@@ -18,7 +18,7 @@ export default async function handler(
// Get the client
const client = await getClient()
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:download:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:download:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Get the ID and table from the query string
@@ -34,7 +34,7 @@ export default async function handler(
return response.status(404).json({ error: 'ID parameter not found' });
}
- const restrictedData = await checkApiPermissions(request, response, session, client, "data:plants:viewRestrictedSections")
+ const restrictedData = await checkApiPermissions(request, response, session, client, makeQuery, "data:plants:viewRestrictedSections")
const data = await downloadPlantData(table, id, client, restrictedData);
if(data[0] === "error"){
diff --git a/website/src/pages/api/plants/json.ts b/website/src/pages/api/plants/json.ts
index 741683b..4b885f6 100644
--- a/website/src/pages/api/plants/json.ts
+++ b/website/src/pages/api/plants/json.ts
@@ -11,7 +11,7 @@ import {
ValidPlantData
} from "@/lib/plant_data";
import axios from "axios";
-import {getClient} from "@/lib/databse";
+import {getClient, makeQuery} from "@/lib/databse";
import {checkApiPermissions} from "@/lib/api_tools";
import {getServerSession} from "next-auth";
import {authOptions} from "@/pages/api/auth/[...nextauth]";
@@ -42,7 +42,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:json:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:json:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try running the operation
@@ -51,7 +51,7 @@ export default async function handler(
switch (operation) {
case 'download':
- const permissionD = await checkApiPermissions(request, response, session, client, "api:plants:json:download")
+ const permissionD = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:json:download")
if(!permissionD) return response.status(401).json({error: "Not Authorized"})
// If the ID is not found, return an error
@@ -64,7 +64,7 @@ export default async function handler(
return response.status(404).json({ error: 'ID parameter is not a number' });
}
- const restrictedData = await checkApiPermissions(request, response, session, client, "data:plants:viewRestrictedSections")
+ const restrictedData = await checkApiPermissions(request, response, session, client, makeQuery, "data:plants:viewRestrictedSections")
// Download the data from the database using the download API with the ID and table
let plantsInfo = await downloadPlantData(["plants", "months_ready_for_use", "edible", "medical", "craft", "source", "custom", "attachments"], Number(id), client, restrictedData)
@@ -120,7 +120,7 @@ export default async function handler(
case 'upload':
- const permissionU = await checkApiPermissions(request, response, session, client, "api:plants:json:upload")
+ const permissionU = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:json:upload")
if(!permissionU) return response.status(401).json({error: "Not Authorized"})
// Check if the JSON param exists
@@ -162,7 +162,7 @@ export default async function handler(
return response.status(200).json({ data: result.data });
case "convert":
- const permissionC = await checkApiPermissions(request, response, session, client, "api:plants:json:convert")
+ const permissionC = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:json:convert")
if(!permissionC) return response.status(401).json({error: "Not Authorized"})
// Check if there is the tableName param
diff --git a/website/src/pages/api/plants/months.ts b/website/src/pages/api/plants/months.ts
index caa33a1..6c41d48 100644
--- a/website/src/pages/api/plants/months.ts
+++ b/website/src/pages/api/plants/months.ts
@@ -20,7 +20,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:months:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:months:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try downloading the data from the database
diff --git a/website/src/pages/api/plants/random.ts b/website/src/pages/api/plants/random.ts
index d6f96d4..c25ae14 100644
--- a/website/src/pages/api/plants/random.ts
+++ b/website/src/pages/api/plants/random.ts
@@ -29,7 +29,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:random:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:random:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try querying the database
diff --git a/website/src/pages/api/plants/remove.ts b/website/src/pages/api/plants/remove.ts
index e5bbeb2..34cb967 100644
--- a/website/src/pages/api/plants/remove.ts
+++ b/website/src/pages/api/plants/remove.ts
@@ -20,7 +20,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:remove:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:remove:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
try {
diff --git a/website/src/pages/api/plants/search.ts b/website/src/pages/api/plants/search.ts
index 1612120..0585cb0 100644
--- a/website/src/pages/api/plants/search.ts
+++ b/website/src/pages/api/plants/search.ts
@@ -19,7 +19,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:search:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:search:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Get the ID and table from the query string
diff --git a/website/src/pages/api/plants/upload.ts b/website/src/pages/api/plants/upload.ts
index f9e4c18..2a93fe9 100644
--- a/website/src/pages/api/plants/upload.ts
+++ b/website/src/pages/api/plants/upload.ts
@@ -24,7 +24,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:upload:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:upload:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try uploading the data to the database
diff --git a/website/src/pages/api/plants/uses.ts b/website/src/pages/api/plants/uses.ts
index 42477c9..193e648 100644
--- a/website/src/pages/api/plants/uses.ts
+++ b/website/src/pages/api/plants/uses.ts
@@ -19,7 +19,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:plants:uses:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:plants:uses:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
// Try downloading the data from the database
diff --git a/website/src/pages/api/user/api_keys.ts b/website/src/pages/api/user/api_keys.ts
index 7b540a5..e2e6721 100644
--- a/website/src/pages/api/user/api_keys.ts
+++ b/website/src/pages/api/user/api_keys.ts
@@ -1,8 +1,9 @@
import {NextApiRequest, NextApiResponse} from 'next';
-import {getClient, getTables} from "@/lib/databse";
+import {getClient, getTables, makeQuery} from "@/lib/databse";
import {getServerSession} from "next-auth";
import {authOptions} from "@/pages/api/auth/[...nextauth]";
import {checkApiPermissions} from "@/lib/api_tools";
+import {RongoaUser} from "@/lib/users";
export default async function handler(
request: NextApiRequest,
@@ -17,9 +18,13 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- let permission = await checkApiPermissions(request, response, session, client, "api:user:api_keys:access")
+ let permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:api_keys:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
+ let query = ''
+
+ const { id, publicUserID } = request.query;
+
try {
// Get the session
@@ -31,32 +36,70 @@ export default async function handler(
}
// Get the user details
- const user_email = session.user.email;
- const user_name = session.user.name;
+ const user = session.user as RongoaUser;
+ const userId = user.database.id;
// Get the operation
- const { operation } = request.query;
+ const { operation, keyName, permissions} = request.query;
if(!operation) {
return response.status(400).json({ error: 'No operation specified'});
}
+ const privateData = await checkApiPermissions(request, response, session, client, makeQuery, "data:account:viewPrivateDetails")
+
switch (operation) {
case "new":
- permission = await checkApiPermissions(request, response, session, client, "api:user:api_keys:add")
+
+ // Check if the user has permission to add a key
+ permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:api_keys:add")
if(!permission) return response.status(401).json({error: "Not Authorized"})
- break
+
+ // Check if the key name and permissions are set
+ if(!keyName || !permissions) return response.status(400).json({ error: 'Missing parameters'});
+
+ // Generate the missing parameters
+ let key = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
+ let logs = [{time: Date.now(), action: "Created"}]
+
+ // Insert the key
+ query = `INSERT INTO apikey (${tables.user_id}, ${tables.api_key_name}, ${tables.api_key_value}, ${tables.api_key_permissions}, ${tables.api_key_logs}, ${tables.api_key_last_used} ) VALUES ('${userId}', '${keyName}', '${key}', '${permissions}', '${JSON.stringify(logs)}', NOW())`;
+ const inserted = await makeQuery(query, client);
+
+ // Return the key
+ return response.status(200).json({ data: { key: key }});
case "remove":
- permission = await checkApiPermissions(request, response, session, client, "api:user:api_keys:remove")
+ permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:api_keys:remove")
if(!permission) return response.status(401).json({error: "Not Authorized"})
break
case "edit":
- permission = await checkApiPermissions(request, response, session, client, "api:user:api_keys:edit")
+ permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:api_keys:edit")
if(!permission) return response.status(401).json({error: "Not Authorized"})
break
+ case "fetch":
+ permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:api_keys:fetch")
+ if(!permission) return response.status(401).json({error: "Not Authorized"})
+
+
+ if(publicUserID && privateData){
+ query = `SELECT * FROM apikey WHERE ${tables.user_id} = '${publicUserID}'`;
+ }else{
+ query = `SELECT * FROM apikey WHERE ${tables.user_id} = '${userId}'`;
+ }
+
+ console.log(query);
+ const keys = await makeQuery(query, client)
+
+ // Check if the user has any keys
+ if(keys.length === 0) {
+ return response.status(404).json({ error: 'No keys found'});
+ }
+
+ // Return the keys
+ return response.status(200).json({ data: keys });
}
diff --git a/website/src/pages/api/user/data.ts b/website/src/pages/api/user/data.ts
index 3c84d8a..3d9d214 100644
--- a/website/src/pages/api/user/data.ts
+++ b/website/src/pages/api/user/data.ts
@@ -22,10 +22,10 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:user:data:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:data:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
- const privateData = await checkApiPermissions(request, response, session, client, "data:account:viewPrivateDetails")
+ const privateData = await checkApiPermissions(request, response, session, client, makeQuery, "data:account:viewPrivateDetails")
try {
@@ -56,7 +56,7 @@ export default async function handler(
}
// Return the user
- return response.status(200).json({ user: user[0] });
+ return response.status(200).json({ data: user[0] });
} catch (error) {
diff --git a/website/src/pages/api/user/delete.ts b/website/src/pages/api/user/delete.ts
index a2fabae..acdd0bc 100644
--- a/website/src/pages/api/user/delete.ts
+++ b/website/src/pages/api/user/delete.ts
@@ -1,5 +1,5 @@
import {NextApiRequest, NextApiResponse} from 'next';
-import {getClient, getTables} from "@/lib/databse";
+import {getClient, getTables, makeQuery} from "@/lib/databse";
import {getServerSession} from "next-auth";
import {authOptions} from "@/pages/api/auth/[...nextauth]";
import {RongoaUser} from "@/lib/users";
@@ -21,7 +21,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:user:delete:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:delete:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
try {
@@ -42,10 +42,10 @@ export default async function handler(
// Remove the user
let query = `DELETE FROM users WHERE ${tables.user_email} = '${user_email}' AND ${tables.user_name} = '${user_name}'`;
console.log(query);
- const removed = await client.query(query);
+ const removed = makeQuery(query, client)
// Return the user
- return response.status(200).json({removed: removed.affectedRows});
+ return response.status(200).json({data : removed});
} catch (error) {
diff --git a/website/src/pages/api/user/email.ts b/website/src/pages/api/user/email.ts
index 46ba535..3b252ad 100644
--- a/website/src/pages/api/user/email.ts
+++ b/website/src/pages/api/user/email.ts
@@ -23,7 +23,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:user:email:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:email:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
try {
@@ -42,7 +42,7 @@ export default async function handler(
}
// Return the user
- return response.status(200).json({ user: user[0] });
+ return response.status(200).json({ data: user[0] });
} catch (error) {
diff --git a/website/src/pages/api/user/plants.ts b/website/src/pages/api/user/plants.ts
index 0cfcd6a..883d5c9 100644
--- a/website/src/pages/api/user/plants.ts
+++ b/website/src/pages/api/user/plants.ts
@@ -24,7 +24,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:user:plants:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:plants:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
const handleGet = async (userID: string) => {
diff --git a/website/src/pages/api/user/update.ts b/website/src/pages/api/user/update.ts
index d7229ac..4b3d32a 100644
--- a/website/src/pages/api/user/update.ts
+++ b/website/src/pages/api/user/update.ts
@@ -28,7 +28,7 @@ export default async function handler(
// Check if the user is permitted to access the API
const session = await getServerSession(request, response, authOptions)
- const permission = await checkApiPermissions(request, response, session, client, "api:user:update:access")
+ const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:update:access")
if(!permission) return response.status(401).json({error: "Not Authorized"})
try {
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx
index 351ff6c..d52c15a 100644
--- a/website/src/pages/index.tsx
+++ b/website/src/pages/index.tsx
@@ -8,7 +8,7 @@
// - Display the user's info on the profile page | DONE
// - Edit the user's info | DONE
// - Permissions for users | DONE
-// - User api keys |
+// - User api keys | Create page CSS, View Logs, Edit Name/Permissions, Delete
// - Rewrite docs for users |
import React, {useEffect, useRef} from "react";
@@ -92,7 +92,7 @@ export default function Home() {
if(!userData.data?.user)
return
- const user = userData.data.user as UserDatabaseDetails
+ const user = userData.data.data as UserDatabaseDetails
update({database: user})
saveToCache("user_data_refreshed", true)
diff --git a/website/src/styles/pages/account/index.module.css b/website/src/styles/pages/account/index.module.css
index feebe54..9c1959b 100644
--- a/website/src/styles/pages/account/index.module.css
+++ b/website/src/styles/pages/account/index.module.css
@@ -131,6 +131,7 @@
.tableContainer{
width: 100%;
+ overflow-x:auto;
}
.dataTable{