New API + API Token Updates
This commit is contained in:
@@ -1,44 +1,67 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../../db';
|
||||
import { categories, members, timeEntries } from '../../../../db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../../db";
|
||||
import { categories, members, timeEntries } from "../../../../db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
export const POST: APIRoute = async ({ locals, redirect, params }) => {
|
||||
export const POST: APIRoute = async ({ request, locals, redirect, params }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = params;
|
||||
let redirectTo: string | undefined;
|
||||
|
||||
const userOrg = await db.select()
|
||||
if (request.headers.get("Content-Type")?.includes("application/json")) {
|
||||
const body = await request.json();
|
||||
redirectTo = body.redirectTo;
|
||||
} else {
|
||||
const formData = await request.formData();
|
||||
redirectTo = formData.get("redirectTo")?.toString();
|
||||
}
|
||||
|
||||
const userOrg = await db
|
||||
.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
.get();
|
||||
|
||||
if (!userOrg) {
|
||||
return new Response('No organization found', { status: 400 });
|
||||
return new Response("No organization found", { status: 400 });
|
||||
}
|
||||
|
||||
const isAdmin = userOrg.role === 'owner' || userOrg.role === 'admin';
|
||||
const isAdmin = userOrg.role === "owner" || userOrg.role === "admin";
|
||||
if (!isAdmin) {
|
||||
return new Response('Forbidden', { status: 403 });
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
}
|
||||
|
||||
const hasEntries = await db.select()
|
||||
const hasEntries = await db
|
||||
.select()
|
||||
.from(timeEntries)
|
||||
.where(eq(timeEntries.categoryId, id!))
|
||||
.get();
|
||||
|
||||
if (hasEntries) {
|
||||
return new Response('Cannot delete category with time entries', { status: 400 });
|
||||
return new Response("Cannot delete category with time entries", {
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
await db.delete(categories)
|
||||
.where(and(
|
||||
eq(categories.id, id!),
|
||||
eq(categories.organizationId, userOrg.organizationId)
|
||||
));
|
||||
await db
|
||||
.delete(categories)
|
||||
.where(
|
||||
and(
|
||||
eq(categories.id, id!),
|
||||
eq(categories.organizationId, userOrg.organizationId),
|
||||
),
|
||||
);
|
||||
|
||||
return redirect('/dashboard/team/settings');
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return redirect(redirectTo || "/dashboard/team/settings");
|
||||
};
|
||||
|
||||
@@ -1,46 +1,72 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../../db';
|
||||
import { categories, members } from '../../../../db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../../db";
|
||||
import { categories, members } from "../../../../db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals, redirect, params }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = params;
|
||||
const formData = await request.formData();
|
||||
const name = formData.get('name')?.toString();
|
||||
const color = formData.get('color')?.toString();
|
||||
let name: string | undefined;
|
||||
let color: string | undefined;
|
||||
let redirectTo: string | undefined;
|
||||
|
||||
if (!name) {
|
||||
return new Response('Name is required', { status: 400 });
|
||||
if (request.headers.get("Content-Type")?.includes("application/json")) {
|
||||
const body = await request.json();
|
||||
name = body.name;
|
||||
color = body.color;
|
||||
redirectTo = body.redirectTo;
|
||||
} else {
|
||||
const formData = await request.formData();
|
||||
name = formData.get("name")?.toString();
|
||||
color = formData.get("color")?.toString();
|
||||
redirectTo = formData.get("redirectTo")?.toString();
|
||||
}
|
||||
|
||||
const userOrg = await db.select()
|
||||
if (!name) {
|
||||
return new Response("Name is required", { status: 400 });
|
||||
}
|
||||
|
||||
const userOrg = await db
|
||||
.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
.get();
|
||||
|
||||
if (!userOrg) {
|
||||
return new Response('No organization found', { status: 400 });
|
||||
return new Response("No organization found", { status: 400 });
|
||||
}
|
||||
|
||||
const isAdmin = userOrg.role === 'owner' || userOrg.role === 'admin';
|
||||
const isAdmin = userOrg.role === "owner" || userOrg.role === "admin";
|
||||
if (!isAdmin) {
|
||||
return new Response('Forbidden', { status: 403 });
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
}
|
||||
|
||||
await db.update(categories)
|
||||
await db
|
||||
.update(categories)
|
||||
.set({
|
||||
name,
|
||||
color: color || null,
|
||||
})
|
||||
.where(and(
|
||||
eq(categories.id, id!),
|
||||
eq(categories.organizationId, userOrg.organizationId)
|
||||
));
|
||||
.where(
|
||||
and(
|
||||
eq(categories.id, id!),
|
||||
eq(categories.organizationId, userOrg.organizationId),
|
||||
),
|
||||
);
|
||||
|
||||
return redirect('/dashboard/team/settings');
|
||||
if (locals.scopes) {
|
||||
return new Response(
|
||||
JSON.stringify({ success: true, id, name, color: color || null }),
|
||||
{
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return redirect(redirectTo || "/dashboard/team/settings");
|
||||
};
|
||||
|
||||
@@ -1,38 +1,59 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../db';
|
||||
import { categories, members } from '../../../db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../db";
|
||||
import { categories, members } from "../../../db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals, redirect }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const name = formData.get('name')?.toString();
|
||||
const color = formData.get('color')?.toString();
|
||||
let name: string | undefined;
|
||||
let color: string | undefined;
|
||||
let redirectTo: string | undefined;
|
||||
|
||||
if (request.headers.get("Content-Type")?.includes("application/json")) {
|
||||
const body = await request.json();
|
||||
name = body.name;
|
||||
color = body.color;
|
||||
redirectTo = body.redirectTo;
|
||||
} else {
|
||||
const formData = await request.formData();
|
||||
name = formData.get("name")?.toString();
|
||||
color = formData.get("color")?.toString();
|
||||
redirectTo = formData.get("redirectTo")?.toString();
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return new Response('Name is required', { status: 400 });
|
||||
return new Response("Name is required", { status: 400 });
|
||||
}
|
||||
|
||||
const userOrg = await db.select()
|
||||
const userOrg = await db
|
||||
.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
.get();
|
||||
|
||||
if (!userOrg) {
|
||||
return new Response('No organization found', { status: 400 });
|
||||
return new Response("No organization found", { status: 400 });
|
||||
}
|
||||
|
||||
const id = nanoid();
|
||||
await db.insert(categories).values({
|
||||
id: nanoid(),
|
||||
id,
|
||||
organizationId: userOrg.organizationId,
|
||||
name,
|
||||
color: color || null,
|
||||
});
|
||||
|
||||
return redirect('/dashboard/team/settings');
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ id, name, color: color || null }), {
|
||||
status: 201,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return redirect(redirectTo || "/dashboard/team/settings");
|
||||
};
|
||||
|
||||
@@ -1,71 +1,100 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../../db';
|
||||
import { clients, members, timeEntries, timeEntryTags } from '../../../../db/schema';
|
||||
import { eq, and, inArray } from 'drizzle-orm';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../../db";
|
||||
import {
|
||||
clients,
|
||||
members,
|
||||
timeEntries,
|
||||
timeEntryTags,
|
||||
} from "../../../../db/schema";
|
||||
import { eq, and, inArray } from "drizzle-orm";
|
||||
|
||||
export const POST: APIRoute = async ({ params, locals, redirect }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return redirect('/login');
|
||||
return redirect("/login");
|
||||
}
|
||||
|
||||
const { id } = params;
|
||||
if (!id) {
|
||||
return new Response('Client ID is required', { status: 400 });
|
||||
return new Response("Client ID is required", { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the client to check organization ownership
|
||||
const client = await db.select()
|
||||
const client = await db
|
||||
.select()
|
||||
.from(clients)
|
||||
.where(eq(clients.id, id))
|
||||
.get();
|
||||
|
||||
if (!client) {
|
||||
return new Response('Client not found', { status: 404 });
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ error: "Client not found" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
return new Response("Client not found", { status: 404 });
|
||||
}
|
||||
|
||||
// Verify user is a member of the organization
|
||||
const membership = await db.select()
|
||||
const membership = await db
|
||||
.select()
|
||||
.from(members)
|
||||
.where(and(
|
||||
eq(members.userId, user.id),
|
||||
eq(members.organizationId, client.organizationId)
|
||||
))
|
||||
.where(
|
||||
and(
|
||||
eq(members.userId, user.id),
|
||||
eq(members.organizationId, client.organizationId),
|
||||
),
|
||||
)
|
||||
.get();
|
||||
|
||||
if (!membership) {
|
||||
return new Response('Not authorized', { status: 403 });
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ error: "Not authorized" }), {
|
||||
status: 403,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
return new Response("Not authorized", { status: 403 });
|
||||
}
|
||||
|
||||
// Find all time entries for this client to clean up tags
|
||||
const clientEntries = await db.select({ id: timeEntries.id })
|
||||
const clientEntries = await db
|
||||
.select({ id: timeEntries.id })
|
||||
.from(timeEntries)
|
||||
.where(eq(timeEntries.clientId, id))
|
||||
.all();
|
||||
|
||||
const entryIds = clientEntries.map(e => e.id);
|
||||
const entryIds = clientEntries.map((e) => e.id);
|
||||
|
||||
if (entryIds.length > 0) {
|
||||
// Delete tags associated with these entries
|
||||
await db.delete(timeEntryTags)
|
||||
await db
|
||||
.delete(timeEntryTags)
|
||||
.where(inArray(timeEntryTags.timeEntryId, entryIds))
|
||||
.run();
|
||||
|
||||
// Delete the time entries
|
||||
await db.delete(timeEntries)
|
||||
.where(eq(timeEntries.clientId, id))
|
||||
.run();
|
||||
await db.delete(timeEntries).where(eq(timeEntries.clientId, id)).run();
|
||||
}
|
||||
|
||||
// Delete the client
|
||||
await db.delete(clients)
|
||||
.where(eq(clients.id, id))
|
||||
.run();
|
||||
await db.delete(clients).where(eq(clients.id, id)).run();
|
||||
|
||||
return redirect('/dashboard/clients');
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return redirect("/dashboard/clients");
|
||||
} catch (error) {
|
||||
console.error('Error deleting client:', error);
|
||||
return new Response('Failed to delete client', { status: 500 });
|
||||
console.error("Error deleting client:", error);
|
||||
if (locals.scopes) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Failed to delete client" }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
}
|
||||
return new Response("Failed to delete client", { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,16 +14,24 @@ export const POST: APIRoute = async ({ request, params, locals, redirect }) => {
|
||||
return new Response("Client ID is required", { status: 400 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const name = formData.get("name") as string;
|
||||
const email = formData.get("email") as string;
|
||||
let name: string | undefined;
|
||||
let email: string | undefined;
|
||||
|
||||
if (request.headers.get("Content-Type")?.includes("application/json")) {
|
||||
const body = await request.json();
|
||||
name = body.name;
|
||||
email = body.email;
|
||||
} else {
|
||||
const formData = await request.formData();
|
||||
name = formData.get("name")?.toString();
|
||||
email = formData.get("email")?.toString();
|
||||
}
|
||||
|
||||
if (!name || name.trim().length === 0) {
|
||||
return new Response("Client name is required", { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
// Get the client to check organization ownership
|
||||
const client = await db
|
||||
.select()
|
||||
.from(clients)
|
||||
@@ -31,10 +39,15 @@ export const POST: APIRoute = async ({ request, params, locals, redirect }) => {
|
||||
.get();
|
||||
|
||||
if (!client) {
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ error: "Client not found" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
return new Response("Client not found", { status: 404 });
|
||||
}
|
||||
|
||||
// Verify user is a member of the organization
|
||||
const membership = await db
|
||||
.select()
|
||||
.from(members)
|
||||
@@ -47,10 +60,15 @@ export const POST: APIRoute = async ({ request, params, locals, redirect }) => {
|
||||
.get();
|
||||
|
||||
if (!membership) {
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ error: "Not authorized" }), {
|
||||
status: 403,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
return new Response("Not authorized", { status: 403 });
|
||||
}
|
||||
|
||||
// Update client
|
||||
await db
|
||||
.update(clients)
|
||||
.set({
|
||||
@@ -60,9 +78,33 @@ export const POST: APIRoute = async ({ request, params, locals, redirect }) => {
|
||||
.where(eq(clients.id, id))
|
||||
.run();
|
||||
|
||||
if (locals.scopes) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
id,
|
||||
name: name.trim(),
|
||||
email: email?.trim() || null,
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return redirect(`/dashboard/clients/${id}`);
|
||||
} catch (error) {
|
||||
console.error("Error updating client:", error);
|
||||
if (locals.scopes) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Failed to update client" }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
}
|
||||
return new Response("Failed to update client", { status: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,38 +1,57 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../db';
|
||||
import { clients, members } from '../../../db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../db";
|
||||
import { clients, members } from "../../../db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals, redirect }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const name = formData.get('name')?.toString();
|
||||
const email = formData.get('email')?.toString();
|
||||
let name: string | undefined;
|
||||
let email: string | undefined;
|
||||
|
||||
if (request.headers.get("Content-Type")?.includes("application/json")) {
|
||||
const body = await request.json();
|
||||
name = body.name;
|
||||
email = body.email;
|
||||
} else {
|
||||
const formData = await request.formData();
|
||||
name = formData.get("name")?.toString();
|
||||
email = formData.get("email")?.toString();
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return new Response('Name is required', { status: 400 });
|
||||
return new Response("Name is required", { status: 400 });
|
||||
}
|
||||
|
||||
const userOrg = await db.select()
|
||||
const userOrg = await db
|
||||
.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
.get();
|
||||
|
||||
if (!userOrg) {
|
||||
return new Response('No organization found', { status: 400 });
|
||||
return new Response("No organization found", { status: 400 });
|
||||
}
|
||||
|
||||
const id = nanoid();
|
||||
|
||||
await db.insert(clients).values({
|
||||
id: nanoid(),
|
||||
id,
|
||||
organizationId: userOrg.organizationId,
|
||||
name,
|
||||
email: email || null,
|
||||
});
|
||||
|
||||
return redirect('/dashboard/clients');
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ id, name, email: email || null }), {
|
||||
status: 201,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return redirect("/dashboard/clients");
|
||||
};
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../db';
|
||||
import { organizations, members } from '../../../db/schema';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../db";
|
||||
import { organizations, members } from "../../../db/schema";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals, redirect }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const name = formData.get('name')?.toString();
|
||||
let name: string | undefined;
|
||||
|
||||
if (request.headers.get("Content-Type")?.includes("application/json")) {
|
||||
const body = await request.json();
|
||||
name = body.name;
|
||||
} else {
|
||||
const formData = await request.formData();
|
||||
name = formData.get("name")?.toString();
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
return new Response('Name is required', { status: 400 });
|
||||
return new Response("Name is required", { status: 400 });
|
||||
}
|
||||
|
||||
const orgId = nanoid();
|
||||
@@ -25,8 +32,15 @@ export const POST: APIRoute = async ({ request, locals, redirect }) => {
|
||||
await db.insert(members).values({
|
||||
userId: user.id,
|
||||
organizationId: orgId,
|
||||
role: 'owner',
|
||||
role: "owner",
|
||||
});
|
||||
|
||||
return redirect('/dashboard');
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ id: orgId, name }), {
|
||||
status: 201,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return redirect("/dashboard");
|
||||
};
|
||||
|
||||
@@ -1,34 +1,43 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { db } from '../../../../db';
|
||||
import { timeEntries } from '../../../../db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../../db";
|
||||
import { timeEntries } from "../../../../db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
export const POST: APIRoute = async ({ params, locals, redirect }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 });
|
||||
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
||||
status: 401,
|
||||
});
|
||||
}
|
||||
|
||||
const entryId = params.id;
|
||||
if (!entryId) {
|
||||
return new Response(JSON.stringify({ error: 'Entry ID required' }), { status: 400 });
|
||||
return new Response(JSON.stringify({ error: "Entry ID required" }), {
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const entry = await db.select()
|
||||
const entry = await db
|
||||
.select()
|
||||
.from(timeEntries)
|
||||
.where(and(
|
||||
eq(timeEntries.id, entryId),
|
||||
eq(timeEntries.userId, user.id)
|
||||
))
|
||||
.where(and(eq(timeEntries.id, entryId), eq(timeEntries.userId, user.id)))
|
||||
.get();
|
||||
|
||||
if (!entry) {
|
||||
return new Response(JSON.stringify({ error: 'Entry not found' }), { status: 404 });
|
||||
return new Response(JSON.stringify({ error: "Entry not found" }), {
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
await db.delete(timeEntries)
|
||||
.where(eq(timeEntries.id, entryId))
|
||||
.run();
|
||||
await db.delete(timeEntries).where(eq(timeEntries.id, entryId)).run();
|
||||
|
||||
return redirect('/dashboard/tracker');
|
||||
if (locals.scopes) {
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return redirect("/dashboard/tracker");
|
||||
};
|
||||
|
||||
40
src/pages/api/user/tokens/[id].ts
Normal file
40
src/pages/api/user/tokens/[id].ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../../db";
|
||||
import { apiTokens } from "../../../../db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
export const DELETE: APIRoute = async ({ params, locals }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
||||
status: 401,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const { id } = params;
|
||||
|
||||
if (!id) {
|
||||
return new Response(JSON.stringify({ error: "Token ID is required" }), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const result = await db
|
||||
.delete(apiTokens)
|
||||
.where(and(eq(apiTokens.id, id), eq(apiTokens.userId, user.id)))
|
||||
.returning();
|
||||
|
||||
if (result.length === 0) {
|
||||
return new Response(JSON.stringify({ error: "Token not found" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
};
|
||||
49
src/pages/api/user/tokens/index.ts
Normal file
49
src/pages/api/user/tokens/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { db } from "../../../../db";
|
||||
import { apiTokens } from "../../../../db/schema";
|
||||
import { generateApiToken, hashToken } from "../../../../lib/api-auth";
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals }) => {
|
||||
const user = locals.user;
|
||||
if (!user) {
|
||||
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
||||
status: 401,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const name = formData.get("name")?.toString();
|
||||
|
||||
if (!name) {
|
||||
return new Response(JSON.stringify({ error: "Name is required" }), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const rawToken = generateApiToken();
|
||||
const hashedToken = hashToken(rawToken);
|
||||
|
||||
const [newToken] = await db
|
||||
.insert(apiTokens)
|
||||
.values({
|
||||
userId: user.id,
|
||||
name,
|
||||
token: hashedToken,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
...newToken,
|
||||
token: rawToken,
|
||||
}),
|
||||
{
|
||||
status: 201,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user