init
This commit is contained in:
40
src/api/lineage/_database_mgmt/loose/route.ts
Normal file
40
src/api/lineage/_database_mgmt/loose/route.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { env } from "@/env.mjs";
|
||||
import { createClient as createAPIClient } from "@tursodatabase/api";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
const IGNORE = ["frenome", "magic-delve-conductor"];
|
||||
|
||||
export async function GET() {
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = "SELECT database_url FROM User WHERE database_url IS NOT NULL";
|
||||
try {
|
||||
const res = await conn.execute(query);
|
||||
const turso = createAPIClient({
|
||||
org: "mikefreno",
|
||||
token: env.TURSO_DB_API_TOKEN,
|
||||
});
|
||||
const linkedDatabaseUrls = res.rows.map((row) => row.database_url);
|
||||
|
||||
const all_dbs = await turso.databases.list();
|
||||
console.log(all_dbs);
|
||||
const dbs_to_delete = all_dbs.filter((db) => {
|
||||
return !IGNORE.includes(db.name) && !linkedDatabaseUrls.includes(db.name);
|
||||
});
|
||||
//console.log("will delete:", dbs_to_delete);
|
||||
} catch (e) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: e,
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
42
src/api/lineage/_database_mgmt/old/route.ts
Normal file
42
src/api/lineage/_database_mgmt/old/route.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
import { createClient as createAPIClient } from "@tursodatabase/api";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
const conn = LineageConnectionFactory();
|
||||
const query =
|
||||
"SELECT * FROM User WHERE datetime(db_destroy_date) < datetime('now');";
|
||||
try {
|
||||
const res = await conn.execute(query);
|
||||
const turso = createAPIClient({
|
||||
org: "mikefreno",
|
||||
token: env.TURSO_DB_API_TOKEN,
|
||||
});
|
||||
|
||||
res.rows.forEach(async (row) => {
|
||||
const db_url = row.database_url;
|
||||
|
||||
await turso.databases.delete(db_url as string);
|
||||
const query =
|
||||
"UPDATE User SET database_url = ?, database_token = ?, db_destroy_date = ? WHERE id = ?";
|
||||
const params = [null, null, null, row.id];
|
||||
conn.execute({ sql: query, args: params });
|
||||
});
|
||||
} catch (e) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: e,
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
42
src/api/lineage/analytics/route.ts
Normal file
42
src/api/lineage/analytics/route.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const {
|
||||
playerID,
|
||||
dungeonProgression,
|
||||
playerClass,
|
||||
spellCount,
|
||||
proficiencies,
|
||||
jobs,
|
||||
resistanceTable,
|
||||
damageTable,
|
||||
} = await req.json();
|
||||
const conn = LineageConnectionFactory();
|
||||
try {
|
||||
const res = await conn.execute({
|
||||
sql: `
|
||||
INSERT OR REPLACE INTO Analytics
|
||||
(playerID, dungeonProgression, playerClass, spellCount, proficiencies, jobs, resistanceTable, damageTable)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
args: [
|
||||
playerID,
|
||||
JSON.stringify(dungeonProgression),
|
||||
playerClass,
|
||||
spellCount,
|
||||
JSON.stringify(proficiencies),
|
||||
JSON.stringify(jobs),
|
||||
JSON.stringify(resistanceTable),
|
||||
JSON.stringify(damageTable),
|
||||
],
|
||||
});
|
||||
console.log(res);
|
||||
|
||||
return NextResponse.json({ status: 200 });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
return NextResponse.json({ status: 500 });
|
||||
}
|
||||
}
|
||||
26
src/api/lineage/apple/email/route.ts
Normal file
26
src/api/lineage/apple/email/route.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { userString } = await req.json();
|
||||
if (!userString) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Missing required fields",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = "SELECT * FROM User WHERE apple_user_string = ?";
|
||||
const res = await conn.execute({ sql: query, args: [userString] });
|
||||
if (res.rows.length > 0) {
|
||||
return NextResponse.json(
|
||||
{ success: true, email: res.rows[0].email },
|
||||
{ status: 200 },
|
||||
);
|
||||
} else {
|
||||
return NextResponse.json({ success: false }, { status: 404 });
|
||||
}
|
||||
}
|
||||
134
src/api/lineage/apple/registration/route.ts
Normal file
134
src/api/lineage/apple/registration/route.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { LineageConnectionFactory, LineageDBInit } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { createClient as createAPIClient } from "@tursodatabase/api";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const { email, userString } = await request.json();
|
||||
if (!userString) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Missing required fields",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
let dbName;
|
||||
let dbToken;
|
||||
const conn = LineageConnectionFactory();
|
||||
|
||||
try {
|
||||
let checkUserQuery = "SELECT * FROM User WHERE apple_user_string = ?";
|
||||
|
||||
let args = [userString];
|
||||
if (email) {
|
||||
args.push(email);
|
||||
checkUserQuery += " OR email = ?";
|
||||
}
|
||||
const checkUserResult = await conn.execute({
|
||||
sql: checkUserQuery,
|
||||
args: args,
|
||||
});
|
||||
|
||||
if (checkUserResult.rows.length > 0) {
|
||||
const setClauses = [];
|
||||
const values = [];
|
||||
|
||||
if (email) {
|
||||
setClauses.push("email = ?");
|
||||
values.push(email);
|
||||
}
|
||||
setClauses.push("provider = ?", "apple_user_string = ?");
|
||||
values.push("apple", userString);
|
||||
const whereClause = `WHERE apple_user_string = ?${
|
||||
email && " OR email = ?"
|
||||
}`;
|
||||
values.push(userString);
|
||||
if (email) {
|
||||
values.push(email);
|
||||
}
|
||||
|
||||
const updateQuery = `UPDATE User SET ${setClauses.join(
|
||||
", ",
|
||||
)} ${whereClause}`;
|
||||
const updateRes = await conn.execute({
|
||||
sql: updateQuery,
|
||||
args: values,
|
||||
});
|
||||
if (updateRes.rowsAffected != 0) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "User information updated",
|
||||
email: checkUserResult.rows[0].email,
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} else {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "User update failed!",
|
||||
}),
|
||||
{ status: 418, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// User doesn't exist, insert new user and init database
|
||||
const dbInit = await LineageDBInit();
|
||||
dbToken = dbInit.token;
|
||||
dbName = dbInit.dbName;
|
||||
|
||||
try {
|
||||
const insertQuery = `
|
||||
INSERT INTO User (email, email_verified, apple_user_string, provider, database_name, database_token)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
await conn.execute({
|
||||
sql: insertQuery,
|
||||
args: [email, true, userString, "apple", dbName, dbToken],
|
||||
});
|
||||
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "New user created",
|
||||
dbName,
|
||||
dbToken,
|
||||
}),
|
||||
{ status: 201, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} catch (error) {
|
||||
const turso = createAPIClient({
|
||||
org: "mikefreno",
|
||||
token: env.TURSO_DB_API_TOKEN,
|
||||
});
|
||||
await turso.databases.delete(dbName);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (dbName) {
|
||||
try {
|
||||
const turso = createAPIClient({
|
||||
org: "mikefreno",
|
||||
token: env.TURSO_DB_API_TOKEN,
|
||||
});
|
||||
await turso.databases.delete(dbName);
|
||||
} catch (deleteErr) {
|
||||
console.error("Error deleting database:", deleteErr);
|
||||
}
|
||||
}
|
||||
console.error("Error in Apple Sign-Up handler:", error);
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "An error occurred while processing the request",
|
||||
}),
|
||||
{ status: 500, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
}
|
||||
89
src/api/lineage/database/creds/route.ts
Normal file
89
src/api/lineage/database/creds/route.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { OAuth2Client } from "google-auth-library";
|
||||
const CLIENT_ID = env.NEXT_PUBLIC_GOOGLE_CLIENT_ID_MAGIC_DELVE;
|
||||
|
||||
const client = new OAuth2Client(CLIENT_ID);
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const authHeader = req.headers.get("authorization");
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return new NextResponse(JSON.stringify({ valid: false }), { status: 401 });
|
||||
}
|
||||
const { email, provider } = await req.json();
|
||||
if (!email) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({ success: false, message: "missing email in body" }),
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
try {
|
||||
let valid_request = false;
|
||||
if (provider == "email") {
|
||||
const decoded = jwt.verify(token, env.JWT_SECRET_KEY) as jwt.JwtPayload;
|
||||
if (decoded.email == email) {
|
||||
valid_request = true;
|
||||
}
|
||||
} else if (provider == "google") {
|
||||
const ticket = await client.verifyIdToken({
|
||||
idToken: token,
|
||||
audience: CLIENT_ID,
|
||||
});
|
||||
if (ticket.getPayload()?.email == email) {
|
||||
valid_request = true;
|
||||
}
|
||||
} else {
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = "SELECT * FROM User WHERE apple_user_string = ?";
|
||||
const res = await conn.execute({ sql: query, args: [token] });
|
||||
if (res.rows.length > 0 && res.rows[0].email == email) {
|
||||
valid_request = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_request) {
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = "SELECT * FROM User WHERE email = ? LIMIT 1";
|
||||
const params = [email];
|
||||
const res = await conn.execute({ sql: query, args: params });
|
||||
if (res.rows.length === 1) {
|
||||
const user = res.rows[0];
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
db_name: user.database_name,
|
||||
db_token: user.database_token,
|
||||
}),
|
||||
{ status: 200 },
|
||||
);
|
||||
}
|
||||
return new NextResponse(
|
||||
JSON.stringify({ success: false, message: "no user found" }),
|
||||
{
|
||||
status: 404,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return new NextResponse(
|
||||
JSON.stringify({ success: false, message: "destroy token" }),
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({ success: false, message: error }),
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
69
src/api/lineage/database/deletion/cancel/route.ts
Normal file
69
src/api/lineage/database/deletion/cancel/route.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { LineageConnectionFactory, validateLineageRequest } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const authHeader = req.headers.get("authorization");
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return NextResponse.json({
|
||||
status: 401,
|
||||
ok: false,
|
||||
message: "Missing or invalid authorization header.",
|
||||
});
|
||||
}
|
||||
const auth_token = authHeader.split(" ")[1];
|
||||
|
||||
const { email } = await req.json();
|
||||
if (!email) {
|
||||
return NextResponse.json({
|
||||
status: 400,
|
||||
ok: false,
|
||||
message: "Email is required to cancel the cron job.",
|
||||
});
|
||||
}
|
||||
|
||||
const conn = LineageConnectionFactory();
|
||||
|
||||
const resUser = await conn.execute({
|
||||
sql: `SELECT * FROM User WHERE email = ?;`,
|
||||
args: [email],
|
||||
});
|
||||
if (resUser.rows.length === 0) {
|
||||
return NextResponse.json({
|
||||
status: 404,
|
||||
ok: false,
|
||||
message: "User not found.",
|
||||
});
|
||||
}
|
||||
const userRow = resUser.rows[0];
|
||||
if (!userRow) {
|
||||
return NextResponse.json({ status: 404, ok: false });
|
||||
}
|
||||
|
||||
const valid = await validateLineageRequest({ auth_token, userRow });
|
||||
if (!valid) {
|
||||
return NextResponse.json({
|
||||
status: 401,
|
||||
ok: false,
|
||||
message: "Invalid credentials for cancelation.",
|
||||
});
|
||||
}
|
||||
|
||||
const result = await conn.execute({
|
||||
sql: `DELETE FROM cron WHERE email = ?;`,
|
||||
args: [email],
|
||||
});
|
||||
|
||||
if (result.rowsAffected > 0) {
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
ok: true,
|
||||
message: "Cron job(s) canceled successfully.",
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({
|
||||
status: 404,
|
||||
ok: false,
|
||||
message: "No cron job found for the given email.",
|
||||
});
|
||||
}
|
||||
}
|
||||
24
src/api/lineage/database/deletion/check/route.ts
Normal file
24
src/api/lineage/database/deletion/check/route.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { email } = await req.json();
|
||||
const conn = LineageConnectionFactory();
|
||||
try {
|
||||
const res = await conn.execute({
|
||||
sql: `SELECT * FROM cron WHERE email = ?`,
|
||||
args: [email],
|
||||
});
|
||||
const cronRow = res.rows[0];
|
||||
if (!cronRow) {
|
||||
return NextResponse.json({ status: 204, ok: true });
|
||||
}
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: 200,
|
||||
created_at: cronRow.created_at,
|
||||
});
|
||||
} catch (e) {
|
||||
return NextResponse.json({ status: 500, ok: false });
|
||||
}
|
||||
}
|
||||
73
src/api/lineage/database/deletion/cron/route.ts
Normal file
73
src/api/lineage/database/deletion/cron/route.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { dumpAndSendDB, LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextResponse } from "next/server";
|
||||
import { createClient as createAPIClient } from "@tursodatabase/api";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export async function GET() {
|
||||
const conn = LineageConnectionFactory();
|
||||
const res = await conn.execute(
|
||||
`SELECT * FROM cron WHERE created_at <= datetime('now', '-1 day');`,
|
||||
);
|
||||
|
||||
if (res.rows.length > 0) {
|
||||
const executed_ids = [];
|
||||
for (const row of res.rows) {
|
||||
const { id, db_name, db_token, send_dump_target, email } = row;
|
||||
|
||||
if (send_dump_target) {
|
||||
const res = await dumpAndSendDB({
|
||||
dbName: db_name as string,
|
||||
dbToken: db_token as string,
|
||||
sendTarget: send_dump_target as string,
|
||||
});
|
||||
if (res.success) {
|
||||
//const res = await turso.databases.delete(db_name as string);
|
||||
//
|
||||
const res = await fetch(
|
||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (res.ok) {
|
||||
executed_ids.push(id);
|
||||
// Shouldn't fail. No idea what the response from turso would be at this point - not documented
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const res = await fetch(
|
||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (res.ok) {
|
||||
conn.execute({
|
||||
sql: `DELETE FROM User WHERE email = ?`,
|
||||
args: [email],
|
||||
});
|
||||
executed_ids.push(id);
|
||||
// Shouldn't fail. No idea what the response from turso would be at this point - not documented
|
||||
}
|
||||
}
|
||||
}
|
||||
if (executed_ids.length > 0) {
|
||||
const placeholders = executed_ids.map(() => "?").join(", ");
|
||||
const deleteQuery = `DELETE FROM cron WHERE id IN (${placeholders});`;
|
||||
await conn.execute({ sql: deleteQuery, args: executed_ids });
|
||||
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
message:
|
||||
"Processed databases deleted and corresponding cron rows removed.",
|
||||
});
|
||||
}
|
||||
}
|
||||
return NextResponse.json({ status: 200, ok: true });
|
||||
}
|
||||
154
src/api/lineage/database/deletion/init/route.ts
Normal file
154
src/api/lineage/database/deletion/init/route.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import {
|
||||
dumpAndSendDB,
|
||||
LineageConnectionFactory,
|
||||
validateLineageRequest,
|
||||
} from "@/app/utils";
|
||||
import { env } from "@/env.mjs";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const authHeader = req.headers.get("authorization");
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return NextResponse.json({ status: 401, ok: false });
|
||||
}
|
||||
|
||||
const auth_token = authHeader.split(" ")[1];
|
||||
const { email, db_name, db_token, skip_cron, send_dump_target } =
|
||||
await req.json();
|
||||
if (!email || !db_name || !db_token || !auth_token) {
|
||||
return NextResponse.json({
|
||||
status: 401,
|
||||
message: "Missing required fields",
|
||||
});
|
||||
}
|
||||
|
||||
const conn = LineageConnectionFactory();
|
||||
const res = await conn.execute({
|
||||
sql: `SELECT * FROM User WHERE email = ?`,
|
||||
args: [email],
|
||||
});
|
||||
const userRow = res.rows[0];
|
||||
if (!userRow) {
|
||||
return NextResponse.json({ status: 404, ok: false });
|
||||
}
|
||||
|
||||
const valid = await validateLineageRequest({ auth_token, userRow });
|
||||
if (!valid) {
|
||||
return NextResponse.json({
|
||||
ok: false,
|
||||
status: 401,
|
||||
message: "Invalid Verification",
|
||||
});
|
||||
}
|
||||
|
||||
const { database_token, database_name } = userRow;
|
||||
|
||||
if (database_token !== db_token || database_name !== db_name) {
|
||||
return NextResponse.json({
|
||||
ok: false,
|
||||
status: 401,
|
||||
message: "Incorrect Verification",
|
||||
});
|
||||
}
|
||||
|
||||
if (skip_cron) {
|
||||
if (send_dump_target) {
|
||||
const res = await dumpAndSendDB({
|
||||
dbName: db_name,
|
||||
dbToken: db_token,
|
||||
sendTarget: send_dump_target,
|
||||
});
|
||||
if (res.success) {
|
||||
//const turso = createAPIClient({
|
||||
//org: "mikefreno",
|
||||
//token: env.TURSO_DB_API_TOKEN,
|
||||
//});
|
||||
//const res = await turso.databases.delete(db_name); // seems unreliable, using rest api instead
|
||||
const res = await fetch(
|
||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (res.ok) {
|
||||
conn.execute({
|
||||
sql: `DELETE FROM User WHERE email = ?`,
|
||||
args: [email],
|
||||
});
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: 200,
|
||||
message: `Account and Database deleted, db dump sent to email: ${send_dump_target}`,
|
||||
});
|
||||
} else {
|
||||
// Shouldn't fail. No idea what the response from turso would be at this point - not documented
|
||||
return NextResponse.json({
|
||||
status: 500,
|
||||
message: "Unknown",
|
||||
ok: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return NextResponse.json({
|
||||
ok: false,
|
||||
status: 500,
|
||||
message: res.reason,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
//const turso = createAPIClient({
|
||||
//org: "mikefreno",
|
||||
//token: env.TURSO_DB_API_TOKEN,
|
||||
//});
|
||||
//const res = await turso.databases.delete(db_name);
|
||||
const res = await fetch(
|
||||
`https://api.turso.tech/v1/organizations/mikefreno/databases/${db_name}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.TURSO_DB_API_TOKEN}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
if (res.ok) {
|
||||
conn.execute({
|
||||
sql: `DELETE FROM User WHERE email = ?`,
|
||||
args: [email],
|
||||
});
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: 200,
|
||||
message: `Account and Database deleted`,
|
||||
});
|
||||
} else {
|
||||
// Shouldn't fail. No idea what the response from turso would be at this point - not documented
|
||||
return NextResponse.json({
|
||||
ok: false,
|
||||
status: 500,
|
||||
message: "Unknown",
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const insertRes = await conn.execute({
|
||||
sql: `INSERT INTO cron (email, db_name, db_token, send_dump_target) VALUES (?, ?, ?, ?)`,
|
||||
args: [email, db_name, db_token, send_dump_target],
|
||||
});
|
||||
if (insertRes.rowsAffected > 0) {
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: 200,
|
||||
message: `Deletion scheduled.`,
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({
|
||||
ok: false,
|
||||
status: 500,
|
||||
message: `Deletion not scheduled, due to server failure`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/api/lineage/email/login/route.ts
Normal file
81
src/api/lineage/email/login/route.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { LINEAGE_JWT_EXPIRY, LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { checkPassword } from "../../../passwordHashing";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
interface InputData {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export async function POST(input: NextRequest) {
|
||||
const inputData = (await input.json()) as InputData;
|
||||
const { email, password } = inputData;
|
||||
if (email && password) {
|
||||
if (password.length < 8) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Invalid Credentials",
|
||||
}),
|
||||
{ status: 401, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = `SELECT * FROM User WHERE email = ? AND provider = ? LIMIT 1`;
|
||||
const params = [email, "email"];
|
||||
const res = await conn.execute({ sql: query, args: params });
|
||||
if (res.rows.length == 0) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Invalid Credentials",
|
||||
}),
|
||||
{ status: 401, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
const user = res.rows[0];
|
||||
if (user.email_verified === 0) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Email not yet verified!",
|
||||
}),
|
||||
{ status: 401, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
const valid = await checkPassword(password, user.password_hash as string);
|
||||
if (!valid) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Invalid Credentials",
|
||||
}),
|
||||
{ status: 401, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
// create token
|
||||
const token = jwt.sign(
|
||||
{ userId: user.id, email: user.email },
|
||||
env.JWT_SECRET_KEY,
|
||||
{ expiresIn: LINEAGE_JWT_EXPIRY },
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: "Login successful",
|
||||
token: token,
|
||||
email: email,
|
||||
});
|
||||
} else {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Missing required fields",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
}
|
||||
33
src/api/lineage/email/refresh/token/route.ts
Normal file
33
src/api/lineage/email/refresh/token/route.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { env } from "@/env.mjs";
|
||||
import { LINEAGE_JWT_EXPIRY } from "@/app/utils";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const authHeader = req.headers.get("authorization");
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return new NextResponse(JSON.stringify({ valid: false }), { status: 401 });
|
||||
}
|
||||
|
||||
const token = authHeader.split(" ")[1];
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, env.JWT_SECRET_KEY) as jwt.JwtPayload;
|
||||
|
||||
const newToken = jwt.sign(
|
||||
{ userId: decoded.userId, email: decoded.email },
|
||||
env.JWT_SECRET_KEY,
|
||||
{ expiresIn: LINEAGE_JWT_EXPIRY },
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
status: 200,
|
||||
ok: true,
|
||||
valid: true,
|
||||
token: newToken,
|
||||
email: decoded.email,
|
||||
});
|
||||
} catch (error) {
|
||||
return NextResponse.json({ status: 401, ok: false });
|
||||
}
|
||||
}
|
||||
107
src/api/lineage/email/refresh/verification/route.ts
Normal file
107
src/api/lineage/email/refresh/verification/route.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { env } from "@/env.mjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
interface InputData {
|
||||
email: string;
|
||||
}
|
||||
export async function POST(input: NextRequest) {
|
||||
const inputData = (await input.json()) as InputData;
|
||||
const { email } = inputData;
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = "SELECT * FROM User WHERE email = ?";
|
||||
const params = [email];
|
||||
|
||||
const res = await conn.execute({ sql: query, args: params });
|
||||
|
||||
if (res.rows.length == 0 || res.rows[0].email_verified) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Invalid Request",
|
||||
}),
|
||||
{ status: 409, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
const email_res = await sendEmailVerification(email);
|
||||
const json = await email_res.json();
|
||||
if (json.messageId) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "Email verification sent!",
|
||||
}),
|
||||
{ status: 201, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} else {
|
||||
return NextResponse.json(json);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendEmailVerification(userEmail: string) {
|
||||
const apiKey = env.SENDINBLUE_KEY as string;
|
||||
const apiUrl = "https://api.sendinblue.com/v3/smtp/email";
|
||||
|
||||
const secretKey = env.JWT_SECRET_KEY;
|
||||
const payload = { email: userEmail };
|
||||
const token = jwt.sign(payload, secretKey, { expiresIn: "15m" });
|
||||
|
||||
const sendinblueData = {
|
||||
sender: {
|
||||
name: "MikeFreno",
|
||||
email: "lifeandlineage_no_reply@freno.me",
|
||||
},
|
||||
to: [
|
||||
{
|
||||
email: userEmail,
|
||||
},
|
||||
],
|
||||
htmlContent: `<html>
|
||||
<head>
|
||||
<style>
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
background-color: #007BFF;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="center">
|
||||
<p>Click the button below to verify email</p>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="center">
|
||||
<a href=${env.NEXT_PUBLIC_DOMAIN}/api/lineage/email/verification/${userEmail}/?token=${token} class="button">Verify Email</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
subject: `Life and Lineage email verification`,
|
||||
};
|
||||
return await fetch(apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"api-key": apiKey,
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(sendinblueData),
|
||||
});
|
||||
}
|
||||
144
src/api/lineage/email/registration/route.ts
Normal file
144
src/api/lineage/email/registration/route.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { hashPassword } from "../../../passwordHashing";
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { env } from "@/env.mjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { LibsqlError } from "@libsql/client/web";
|
||||
|
||||
interface InputData {
|
||||
email: string;
|
||||
password: string;
|
||||
password_conf: string;
|
||||
}
|
||||
|
||||
export async function POST(input: NextRequest) {
|
||||
const inputData = (await input.json()) as InputData;
|
||||
const { email, password, password_conf } = inputData;
|
||||
|
||||
if (email && password && password_conf) {
|
||||
if (password == password_conf) {
|
||||
const passwordHash = await hashPassword(password);
|
||||
const conn = LineageConnectionFactory();
|
||||
const userCreationQuery = `
|
||||
INSERT INTO User (email, provider, password_hash)
|
||||
VALUES (?, ?, ?)
|
||||
`;
|
||||
const params = [email, "email", passwordHash];
|
||||
try {
|
||||
await conn.execute({ sql: userCreationQuery, args: params });
|
||||
|
||||
const res = await sendEmailVerification(email);
|
||||
const json = await res.json();
|
||||
if (json.messageId) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "Email verification sent!",
|
||||
}),
|
||||
{ status: 201, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} else {
|
||||
return NextResponse.json(json);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e instanceof LibsqlError && e.code === "SQLITE_CONSTRAINT") {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "User already exists",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "An error occurred while creating the user",
|
||||
}),
|
||||
{ status: 500, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
}
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Password mismatch",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Missing required fields",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
async function sendEmailVerification(userEmail: string) {
|
||||
const apiKey = env.SENDINBLUE_KEY as string;
|
||||
const apiUrl = "https://api.sendinblue.com/v3/smtp/email";
|
||||
|
||||
const secretKey = env.JWT_SECRET_KEY;
|
||||
const payload = { email: userEmail };
|
||||
const token = jwt.sign(payload, secretKey, { expiresIn: "15m" });
|
||||
|
||||
const sendinblueData = {
|
||||
sender: {
|
||||
name: "MikeFreno",
|
||||
email: "lifeandlineage_no_reply@freno.me",
|
||||
},
|
||||
to: [
|
||||
{
|
||||
email: userEmail,
|
||||
},
|
||||
],
|
||||
htmlContent: `<html>
|
||||
<head>
|
||||
<style>
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: #ffffff;
|
||||
background-color: #007BFF;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="center">
|
||||
<p>Click the button below to verify email</p>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="center">
|
||||
<a href=${env.NEXT_PUBLIC_DOMAIN}/api/lineage/email/verification/${userEmail}/?token=${token} class="button">Verify Email</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
subject: `Life and Lineage email verification`,
|
||||
};
|
||||
return await fetch(apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"api-key": apiKey,
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(sendinblueData),
|
||||
});
|
||||
}
|
||||
96
src/api/lineage/email/verification/[email]/route.ts
Normal file
96
src/api/lineage/email/verification/[email]/route.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { env } from "@/env.mjs";
|
||||
import jwt, { JwtPayload } from "jsonwebtoken";
|
||||
import { LineageConnectionFactory, LineageDBInit } from "@/app/utils";
|
||||
import { createClient as createAPIClient } from "@tursodatabase/api";
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
context: { params: Promise<{ email: string }> },
|
||||
) {
|
||||
const secretKey = env.JWT_SECRET_KEY;
|
||||
const params = request.nextUrl.searchParams;
|
||||
const token = params.get("token");
|
||||
const userEmail = (await context.params).email;
|
||||
|
||||
let conn;
|
||||
let dbName;
|
||||
let dbToken;
|
||||
|
||||
try {
|
||||
if (!token) {
|
||||
return NextResponse.json(
|
||||
{ success: false, message: "Authentication failed: no token" },
|
||||
{ status: 401, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
const decoded = jwt.verify(token, secretKey) as JwtPayload;
|
||||
if (decoded.email !== userEmail) {
|
||||
return NextResponse.json(
|
||||
{ success: false, message: "Authentication failed: email mismatch" },
|
||||
{ status: 401, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
conn = LineageConnectionFactory();
|
||||
const dbInit = await LineageDBInit();
|
||||
dbName = dbInit.dbName;
|
||||
dbToken = dbInit.token;
|
||||
|
||||
const query = `UPDATE User SET email_verified = ?, database_name = ?, database_token = ? WHERE email = ?`;
|
||||
const queryParams = [true, dbName, dbToken, userEmail];
|
||||
const res = await conn.execute({ sql: query, args: queryParams });
|
||||
|
||||
if (res.rowsAffected === 0) {
|
||||
throw new Error("User not found or update failed");
|
||||
}
|
||||
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message:
|
||||
"Email verification success. You may close this window and sign in within the app.",
|
||||
}),
|
||||
{ status: 202, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("Error in email verification:", err);
|
||||
|
||||
// Delete the database if it was created
|
||||
if (dbName) {
|
||||
try {
|
||||
const turso = createAPIClient({
|
||||
org: "mikefreno",
|
||||
token: env.TURSO_DB_API_TOKEN,
|
||||
});
|
||||
await turso.databases.delete(dbName);
|
||||
console.log(`Database ${dbName} deleted due to error`);
|
||||
} catch (deleteErr) {
|
||||
console.error("Error deleting database:", deleteErr);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to revert the User table update if conn is available
|
||||
if (conn) {
|
||||
try {
|
||||
await conn.execute({
|
||||
sql: `UPDATE User SET email_verified = ?, database_name = ?, database_token = ? WHERE email = ?`,
|
||||
args: [false, null, null, userEmail],
|
||||
});
|
||||
console.log("User table update reverted");
|
||||
} catch (revertErr) {
|
||||
console.error("Error reverting User table update:", revertErr);
|
||||
}
|
||||
}
|
||||
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message:
|
||||
"Authentication failed: An error occurred during email verification. Please try again.",
|
||||
}),
|
||||
{ status: 500, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
}
|
||||
101
src/api/lineage/google/registration/route.ts
Normal file
101
src/api/lineage/google/registration/route.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { LineageConnectionFactory, LineageDBInit } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { createClient as createAPIClient } from "@tursodatabase/api";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const { email } = await request.json();
|
||||
if (!email) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Missing required fields",
|
||||
}),
|
||||
{ status: 400, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
const conn = LineageConnectionFactory();
|
||||
|
||||
try {
|
||||
// Check if the user exists
|
||||
const checkUserQuery = "SELECT * FROM User WHERE email = ?";
|
||||
const checkUserResult = await conn.execute({
|
||||
sql: checkUserQuery,
|
||||
args: [email],
|
||||
});
|
||||
|
||||
if (checkUserResult.rows.length > 0) {
|
||||
const updateQuery = `
|
||||
UPDATE User
|
||||
SET provider = ?
|
||||
WHERE email = ?
|
||||
`;
|
||||
const updateRes = await conn.execute({
|
||||
sql: updateQuery,
|
||||
args: ["google", email],
|
||||
});
|
||||
|
||||
if (updateRes.rowsAffected != 0) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "User information updated",
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} else {
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "User update failed!",
|
||||
}),
|
||||
{ status: 418, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// User doesn't exist, insert new user and init database
|
||||
let db_name;
|
||||
try {
|
||||
const { token, dbName } = await LineageDBInit();
|
||||
db_name = dbName;
|
||||
console.log("init success");
|
||||
const insertQuery = `
|
||||
INSERT INTO User (email, email_verified, provider, database_name, database_token)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`;
|
||||
await conn.execute({
|
||||
sql: insertQuery,
|
||||
args: [email, true, "google", dbName, token],
|
||||
});
|
||||
|
||||
console.log("insert success");
|
||||
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "New user created",
|
||||
}),
|
||||
{ status: 201, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
} catch (error) {
|
||||
const turso = createAPIClient({
|
||||
org: "mikefreno",
|
||||
token: env.TURSO_DB_API_TOKEN,
|
||||
});
|
||||
await turso.databases.delete(db_name!);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in Google Sign-Up handler:", error);
|
||||
return new NextResponse(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "An error occurred while processing the request",
|
||||
}),
|
||||
{ status: 500, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}
|
||||
}
|
||||
27
src/api/lineage/json_service/attacks/route.ts
Normal file
27
src/api/lineage/json_service/attacks/route.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import playerAttacks from "@/lineage-json/attack-route/playerAttacks.json";
|
||||
import mageBooks from "@/lineage-json/attack-route/mageBooks.json";
|
||||
import mageSpells from "@/lineage-json/attack-route/mageSpells.json";
|
||||
import necroBooks from "@/lineage-json/attack-route/necroBooks.json";
|
||||
import necroSpells from "@/lineage-json/attack-route/necroSpells.json";
|
||||
import rangerBooks from "@/lineage-json/attack-route/rangerBooks.json";
|
||||
import rangerSpells from "@/lineage-json/attack-route/rangerSpells.json";
|
||||
import paladinBooks from "@/lineage-json/attack-route/paladinBooks.json";
|
||||
import paladinSpells from "@/lineage-json/attack-route/paladinSpells.json";
|
||||
import summons from "@/lineage-json/attack-route/summons.json";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
playerAttacks,
|
||||
mageBooks,
|
||||
mageSpells,
|
||||
necroBooks,
|
||||
necroSpells,
|
||||
rangerBooks,
|
||||
rangerSpells,
|
||||
paladinBooks,
|
||||
paladinSpells,
|
||||
summons,
|
||||
});
|
||||
}
|
||||
13
src/api/lineage/json_service/conditions/route.ts
Normal file
13
src/api/lineage/json_service/conditions/route.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import conditions from "@/lineage-json/conditions-route/conditions.json";
|
||||
import debilitations from "@/lineage-json/conditions-route/debilitations.json";
|
||||
import sanityDebuffs from "@/lineage-json/conditions-route/sanityDebuffs.json";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
conditions,
|
||||
debilitations,
|
||||
sanityDebuffs,
|
||||
});
|
||||
}
|
||||
7
src/api/lineage/json_service/dungeons/route.ts
Normal file
7
src/api/lineage/json_service/dungeons/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import dungeons from "@/lineage-json/dungeon-route/dungeons.json";
|
||||
import specialEncounters from "@/lineage-json/dungeon-route/specialEncounters.json";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ ok: true, dungeons, specialEncounters });
|
||||
}
|
||||
8
src/api/lineage/json_service/enemies/route.ts
Normal file
8
src/api/lineage/json_service/enemies/route.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import bosses from "@/lineage-json/enemy-route/bosses.json";
|
||||
import enemies from "@/lineage-json/enemy-route/enemy.json";
|
||||
import enemyAttacks from "@/lineage-json/enemy-route/enemyAttacks.json";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ ok: true, bosses, enemies, enemyAttacks });
|
||||
}
|
||||
45
src/api/lineage/json_service/items/route.ts
Normal file
45
src/api/lineage/json_service/items/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import arrows from "@/lineage-json/item-route/arrows.json";
|
||||
import bows from "@/lineage-json/item-route/bows.json";
|
||||
import foci from "@/lineage-json/item-route/foci.json";
|
||||
import hats from "@/lineage-json/item-route/hats.json";
|
||||
import junk from "@/lineage-json/item-route/junk.json";
|
||||
import melee from "@/lineage-json/item-route/melee.json";
|
||||
import robes from "@/lineage-json/item-route/robes.json";
|
||||
import wands from "@/lineage-json/item-route/wands.json";
|
||||
import ingredients from "@/lineage-json/item-route/ingredients.json";
|
||||
import storyItems from "@/lineage-json/item-route/storyItems.json";
|
||||
import artifacts from "@/lineage-json/item-route/artifacts.json";
|
||||
import shields from "@/lineage-json/item-route/shields.json";
|
||||
import bodyArmor from "@/lineage-json/item-route/bodyArmor.json";
|
||||
import helmets from "@/lineage-json/item-route/helmets.json";
|
||||
import suffix from "@/lineage-json/item-route/suffix.json";
|
||||
import prefix from "@/lineage-json/item-route/prefix.json";
|
||||
import potions from "@/lineage-json/item-route/potions.json";
|
||||
import poison from "@/lineage-json/item-route/poison.json";
|
||||
import staves from "@/lineage-json/item-route/staves.json";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
arrows,
|
||||
bows,
|
||||
foci,
|
||||
hats,
|
||||
junk,
|
||||
melee,
|
||||
robes,
|
||||
wands,
|
||||
ingredients,
|
||||
storyItems,
|
||||
artifacts,
|
||||
shields,
|
||||
bodyArmor,
|
||||
helmets,
|
||||
suffix,
|
||||
prefix,
|
||||
potions,
|
||||
poison,
|
||||
staves,
|
||||
});
|
||||
}
|
||||
23
src/api/lineage/json_service/misc/route.ts
Normal file
23
src/api/lineage/json_service/misc/route.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import activities from "@/lineage-json/misc-route/activities.json";
|
||||
import investments from "@/lineage-json/misc-route/investments.json";
|
||||
import jobs from "@/lineage-json/misc-route/jobs.json";
|
||||
import manaOptions from "@/lineage-json/misc-route/manaOptions.json";
|
||||
import otherOptions from "@/lineage-json/misc-route/otherOptions.json";
|
||||
import healthOptions from "@/lineage-json/misc-route/healthOptions.json";
|
||||
import sanityOptions from "@/lineage-json/misc-route/sanityOptions.json";
|
||||
import pvpRewards from "@/lineage-json/misc-route/pvpRewards.json";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
activities,
|
||||
investments,
|
||||
jobs,
|
||||
manaOptions,
|
||||
otherOptions,
|
||||
healthOptions,
|
||||
sanityOptions,
|
||||
pvpRewards,
|
||||
});
|
||||
}
|
||||
5
src/api/lineage/offline_secret/route.ts
Normal file
5
src/api/lineage/offline_secret/route.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
return new NextResponse(process.env.LINEAGE_OFFLINE_SERIALIZATION_SECRET);
|
||||
}
|
||||
28
src/api/lineage/pvp/battle_result/route.ts
Normal file
28
src/api/lineage/pvp/battle_result/route.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { winnerLinkID, loserLinkID } = await req.json();
|
||||
|
||||
const conn = LineageConnectionFactory();
|
||||
|
||||
try {
|
||||
await conn.execute({
|
||||
sql: `
|
||||
UPDATE PvP_Characters
|
||||
SET
|
||||
winCount = winCount + CASE WHEN linkID = ? THEN 1 ELSE 0 END,
|
||||
lossCount = lossCount + CASE WHEN linkID = ? THEN 1 ELSE 0 END
|
||||
WHERE linkID IN (?, ?)
|
||||
`,
|
||||
args: [winnerLinkID, loserLinkID, winnerLinkID, loserLinkID],
|
||||
});
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
status: 200,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return NextResponse.json({ ok: false, status: 500 });
|
||||
}
|
||||
}
|
||||
154
src/api/lineage/pvp/route.ts
Normal file
154
src/api/lineage/pvp/route.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { character, linkID, pushToken, pushCurrentlyEnabled } =
|
||||
await req.json();
|
||||
|
||||
try {
|
||||
const conn = LineageConnectionFactory();
|
||||
const res = await conn.execute({
|
||||
sql: `SELECT * FROM PvP_Characters WHERE linkID = ?`,
|
||||
args: [linkID],
|
||||
});
|
||||
if (res.rows.length == 0) {
|
||||
//create
|
||||
await conn.execute({
|
||||
sql: `INSERT INTO PvP_Characters (
|
||||
linkID,
|
||||
blessing,
|
||||
playerClass,
|
||||
name,
|
||||
maxHealth,
|
||||
maxSanity,
|
||||
maxMana,
|
||||
baseManaRegen,
|
||||
strength,
|
||||
intelligence,
|
||||
dexterity,
|
||||
resistanceTable,
|
||||
damageTable,
|
||||
attackStrings,
|
||||
knownSpells,
|
||||
pushToken,
|
||||
pushCurrentlyEnabled
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
args: [
|
||||
linkID,
|
||||
character.playerClass,
|
||||
character.name,
|
||||
character.maxHealth,
|
||||
character.maxSanity,
|
||||
character.maxMana,
|
||||
character.baseManaRegen,
|
||||
character.strength,
|
||||
character.intelligence,
|
||||
character.dexterity,
|
||||
character.resistanceTable,
|
||||
character.damageTable,
|
||||
character.attackStrings,
|
||||
character.knownSpells,
|
||||
pushToken,
|
||||
pushCurrentlyEnabled,
|
||||
],
|
||||
});
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
winCount: 0,
|
||||
lossCount: 0,
|
||||
tokenRedemptionCount: 0,
|
||||
status: 201,
|
||||
});
|
||||
} else {
|
||||
//update
|
||||
await conn.execute({
|
||||
sql: `UPDATE PvP_Characters SET
|
||||
playerClass = ?,
|
||||
blessing = ?,
|
||||
name = ?,
|
||||
maxHealth = ?,
|
||||
maxSanity = ?,
|
||||
maxMana = ?,
|
||||
baseManaRegen = ?,
|
||||
strength = ?,
|
||||
intelligence = ?,
|
||||
dexterity = ?,
|
||||
resistanceTable = ?,
|
||||
damageTable = ?,
|
||||
attackStrings = ?,
|
||||
knownSpells = ?,
|
||||
pushToken = ?,
|
||||
pushCurrentlyEnabled = ?
|
||||
WHERE linkID = ?`,
|
||||
args: [
|
||||
character.playerClass,
|
||||
character.blessing,
|
||||
character.name,
|
||||
character.maxHealth,
|
||||
character.maxSanity,
|
||||
character.maxMana,
|
||||
character.baseManaRegen,
|
||||
character.strength,
|
||||
character.intelligence,
|
||||
character.dexterity,
|
||||
character.resistanceTable,
|
||||
character.damageTable,
|
||||
character.attackStrings,
|
||||
character.knownSpells,
|
||||
pushToken,
|
||||
pushCurrentlyEnabled,
|
||||
linkID,
|
||||
],
|
||||
});
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
winCount: res.rows[0].winCount,
|
||||
lossCount: res.rows[0].lossCount,
|
||||
tokenRedemptionCount: res.rows[0].tokenRedemptionCount,
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return NextResponse.json({ ok: false, status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
// Get three opponents, high, med, low, based on win/loss ratio
|
||||
const conn = LineageConnectionFactory();
|
||||
try {
|
||||
const res = await conn.execute(
|
||||
`
|
||||
SELECT playerClass,
|
||||
blessing,
|
||||
name,
|
||||
maxHealth,
|
||||
maxSanity,
|
||||
maxMana,
|
||||
baseManaRegen,
|
||||
strength,
|
||||
intelligence,
|
||||
dexterity,
|
||||
resistanceTable,
|
||||
damageTable,
|
||||
attackStrings,
|
||||
knownSpells,
|
||||
linkID,
|
||||
winCount,
|
||||
lossCount
|
||||
FROM PvP_Characters
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 3
|
||||
`,
|
||||
);
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
characters: res.rows,
|
||||
status: 200,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return NextResponse.json({ ok: false, status: 500 });
|
||||
}
|
||||
}
|
||||
27
src/api/lineage/tokens/route.ts
Normal file
27
src/api/lineage/tokens/route.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { LineageConnectionFactory } from "@/app/utils";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { token } = await req.json();
|
||||
if (!token) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({ success: false, message: "missing token in body" }),
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
);
|
||||
}
|
||||
const conn = LineageConnectionFactory();
|
||||
const query = "SELECT * FROM Token WHERE token = ?";
|
||||
const res = await conn.execute({ sql: query, args: [token] });
|
||||
if (res.rows.length > 0) {
|
||||
const queryUpdate =
|
||||
"UPDATE Token SET last_updated_at = datetime('now') WHERE token = ?";
|
||||
const resUpdate = await conn.execute({ sql: queryUpdate, args: [token] });
|
||||
return NextResponse.json(JSON.stringify(resUpdate));
|
||||
} else {
|
||||
const queryInsert = "INSERT INTO Token (token) VALUES (?)";
|
||||
const resInsert = await conn.execute({ sql: queryInsert, args: [token] });
|
||||
return NextResponse.json(JSON.stringify(resInsert));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user