filesystem routing
This commit is contained in:
parent
10e31e2c2f
commit
9157d247ea
19 changed files with 348 additions and 310 deletions
|
@ -1,16 +1,16 @@
|
|||
// Identity. Store your memories and mental belongings
|
||||
// Copyright (C) 2024 Sofía Aritz
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
@ -18,4 +18,4 @@ import Fastify from "fastify";
|
|||
|
||||
export default Fastify({
|
||||
logger: true,
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,28 +1,31 @@
|
|||
// Identity. Store your memories and mental belongings
|
||||
// Copyright (C) 2024 Sofía Aritz
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import "dotenv/config"
|
||||
import "dotenv/config";
|
||||
import app from "./app.js";
|
||||
|
||||
const REQUIRED_VARS = ["IDENTITY_API_JWT_SECRET", "IDENTITY_API_ASSET_API_ENDPOINT", "IDENTITY_API_JWT_ALG"];
|
||||
|
||||
REQUIRED_VARS.forEach(element => {
|
||||
if (process.env[element] == null || (typeof process.env[element] === "string" && process.env[element].length === 0)) {
|
||||
REQUIRED_VARS.forEach((element) => {
|
||||
if (
|
||||
process.env[element] == null ||
|
||||
(typeof process.env[element] === "string" && process.env[element].length === 0)
|
||||
) {
|
||||
app.log.error(`Required environment variable was not set: ${element}`);
|
||||
app.close().then(() => process.exit(1))
|
||||
app.close().then(() => process.exit(1));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -31,4 +34,5 @@ export const JWT_SECRET = new TextEncoder().encode(process.env["IDENTITY_API_JWT
|
|||
export const JWT_ALG = process.env["IDENTITY_API_JWT_ALG"];
|
||||
export const LISTEN_PORT = process.env["IDENTITY_API_LISTEN_PORT"] || 3000;
|
||||
export const ASSET_API_ENDPOINT = process.env["IDENTITY_API_ASSET_API_ENDPOINT"];
|
||||
export const ASSET_API_M2M_REFRESH_INTERVAL = process.env["IDENTITY_API_ASSET_API_M2M_REFRESH_INTERVAL_MS"] || 60 * 1000
|
||||
export const ASSET_API_M2M_REFRESH_INTERVAL =
|
||||
process.env["IDENTITY_API_ASSET_API_M2M_REFRESH_INTERVAL_MS"] || 60 * 1000;
|
||||
|
|
|
@ -1,304 +1,45 @@
|
|||
// Identity. Store your memories and mental belongings
|
||||
// Copyright (C) 2024 Sofía Aritz
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import app from "./app.js"
|
||||
import { ASSET_API_ENDPOINT, IDENTITY_API_LANDING_MESSAGE, LISTEN_PORT } from "./consts.js"
|
||||
import { contentFromSigned, verifySignature } from "./m2m.js";
|
||||
import app from "./app.js";
|
||||
import { IDENTITY_API_LANDING_MESSAGE, LISTEN_PORT } from "./consts.js";
|
||||
import { startAuth } from "./auth.js";
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
import cors from "@fastify/cors";
|
||||
import { registerRoutes } from "./routes/index.js";
|
||||
|
||||
let auth = await startAuth();
|
||||
|
||||
app.addSchema({
|
||||
$id: "schema://identity/authorization",
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
});
|
||||
|
||||
app.register(cors, {
|
||||
origin: true,
|
||||
})
|
||||
});
|
||||
|
||||
registerRoutes(app, auth);
|
||||
|
||||
app.get("/", async () => {
|
||||
return IDENTITY_API_LANDING_MESSAGE;
|
||||
});
|
||||
|
||||
app.put("/m2m/asset", {
|
||||
async handler(request, reply) {
|
||||
if (!verifySignature(request.body)) {
|
||||
reply.statusCode(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let body = JSON.parse(contentFromSigned(request.body));
|
||||
|
||||
let user = await auth.findUserBySessionKey(body.session_key)
|
||||
user.assets.push(body.asset_id);
|
||||
|
||||
await auth.updateUser(user.uid, user);
|
||||
app.log.info((await auth.findUserBySessionKey(body.session_key)).assets);
|
||||
},
|
||||
});
|
||||
|
||||
app.post("/m2m/account", {
|
||||
async handler(request, reply) {
|
||||
if (!verifySignature(request.body)) {
|
||||
reply.statusCode(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let body = JSON.parse(contentFromSigned(request.body));
|
||||
|
||||
let user = await auth.findUserBySessionKey(body.session_key)
|
||||
user.password = undefined;
|
||||
user.entries = undefined;
|
||||
|
||||
return user;
|
||||
},
|
||||
});
|
||||
|
||||
app.get("/asset/endpoint", {
|
||||
async handler() {
|
||||
return ASSET_API_ENDPOINT
|
||||
}
|
||||
})
|
||||
|
||||
app.delete("/entry", {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
user.entries = user.entries.filter(v => v.id !== request.query.entry_id);
|
||||
|
||||
await auth.updateUser(payload.uid, user);
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
},
|
||||
query: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entry_id: { type: "string" },
|
||||
},
|
||||
required: ["entry_id"],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
app.put("/entry", {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
request.body.entry.id = randomUUID().toString();
|
||||
user.entries = [request.body.entry, ...user.entries];
|
||||
|
||||
await auth.updateUser(payload.uid, user);
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
},
|
||||
body: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entry: { type: "object" },
|
||||
},
|
||||
required: ["entry"],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
app.get("/entry/list", {
|
||||
async handler(request, reply) {
|
||||
if (request.query.offset < 0 || request.query.limit <= 0) {
|
||||
reply.status(400);
|
||||
return [];
|
||||
}
|
||||
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
return user.entries.slice(request.query.offset, request.query.offset + request.query.limit);
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
},
|
||||
query: {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: { type: "number" },
|
||||
offset: { type: "number" },
|
||||
},
|
||||
required: ["limit", "offset"],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
app.post("/auth/heirs", {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
if (payload.uid == null) {
|
||||
reply.status(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
user.heirs = request.body;
|
||||
|
||||
await auth.updateUser(payload.uid, user);
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
},
|
||||
body: {
|
||||
type: "array",
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.get("/auth/genkey", {
|
||||
async handler(request) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let session_key = await auth.createSessionKey(payload.uid);
|
||||
|
||||
return {
|
||||
session_key,
|
||||
};
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.get("/auth/account", {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
if (payload.uid == null) {
|
||||
reply.status(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
user.password = undefined;
|
||||
user.entries = undefined;
|
||||
return user;
|
||||
},
|
||||
schema: {
|
||||
headers: {
|
||||
type: "object",
|
||||
properties: {
|
||||
authorization: { type: "string" },
|
||||
},
|
||||
required: ["authorization"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.post("/auth/login", {
|
||||
async handler(request, reply) {
|
||||
let user = await auth.findUserByEmail(request.body.email);
|
||||
|
||||
if (user != null && user.password == request.body.password) {
|
||||
let token = await auth.createJwt(user.uid);
|
||||
|
||||
return {
|
||||
token,
|
||||
};
|
||||
}
|
||||
|
||||
reply.code(400);
|
||||
return {
|
||||
error: "invalid credentials",
|
||||
};
|
||||
},
|
||||
schema: {
|
||||
body: {
|
||||
type: "object",
|
||||
properties: {
|
||||
email: { type: "string" },
|
||||
password: { type: "string" },
|
||||
},
|
||||
},
|
||||
required: ["email", "password"],
|
||||
},
|
||||
});
|
||||
|
||||
app.post("/auth/register", {
|
||||
async handler(request, reply) {
|
||||
if (await auth.findUserByEmail(request.body.email) == null) {
|
||||
let user = await auth.addUser({
|
||||
email: request.body.email,
|
||||
password: request.body.password,
|
||||
name: request.body.name,
|
||||
assets: [],
|
||||
limits: {
|
||||
assetCount: 5,
|
||||
},
|
||||
entries: [],
|
||||
});
|
||||
|
||||
return { token: await auth.createJwt(user.uid) };
|
||||
}
|
||||
|
||||
reply.code(400);
|
||||
return {
|
||||
error: "invalid data",
|
||||
};
|
||||
},
|
||||
schema: {
|
||||
body: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
email: { type: "string" },
|
||||
password: { type: "string" },
|
||||
},
|
||||
required: ["name", "email", "password"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.listen({ port: LISTEN_PORT });
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
// Identity. Store your memories and mental belongings
|
||||
// Copyright (C) 2024 Sofía Aritz
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import { createVerify } from "node:crypto";
|
||||
import assert from "node:assert"
|
||||
import assert from "node:assert";
|
||||
import app from "./app.js";
|
||||
import { ASSET_API_ENDPOINT, ASSET_API_M2M_REFRESH_INTERVAL } from "./consts.js";
|
||||
|
||||
let assetPubKey = await fetchAssetPubkey()
|
||||
let assetAlgorithm = await fetchAssetAlgorithm()
|
||||
let assetPubKey = await fetchAssetPubkey();
|
||||
let assetAlgorithm = await fetchAssetAlgorithm();
|
||||
|
||||
setInterval(async () => {
|
||||
try {
|
||||
|
@ -29,7 +29,7 @@ setInterval(async () => {
|
|||
|
||||
if (pubkey != null && algo != null) {
|
||||
if (assetPubKey !== pubkey) {
|
||||
app.log.warn("The M2M public key has changed!")
|
||||
app.log.warn("The M2M public key has changed!");
|
||||
}
|
||||
|
||||
if (assetAlgorithm !== algo) {
|
||||
|
@ -46,7 +46,7 @@ setInterval(async () => {
|
|||
app.log.warn("Failed to update the M2M credentials");
|
||||
app.log.warn(e);
|
||||
}
|
||||
}, ASSET_API_M2M_REFRESH_INTERVAL)
|
||||
}, ASSET_API_M2M_REFRESH_INTERVAL);
|
||||
|
||||
async function fetchAssetPubkey() {
|
||||
let url = new URL(ASSET_API_ENDPOINT);
|
||||
|
@ -72,11 +72,11 @@ function partsFromSigned(content) {
|
|||
|
||||
assert(parts.length === 2);
|
||||
|
||||
return parts
|
||||
return parts;
|
||||
}
|
||||
|
||||
export function verifySignature(content) {
|
||||
let parts = partsFromSigned(content)
|
||||
let parts = partsFromSigned(content);
|
||||
|
||||
let verify = createVerify(assetAlgorithm);
|
||||
verify.update(parts[0]);
|
||||
|
@ -89,4 +89,4 @@ export function verifySignature(content) {
|
|||
|
||||
export function contentFromSigned(content) {
|
||||
return partsFromSigned(content)[0];
|
||||
}
|
||||
}
|
||||
|
|
9
identity-api/src/routes/asset/endpoint.js
Normal file
9
identity-api/src/routes/asset/endpoint.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { ASSET_API_ENDPOINT } from "../../consts.js";
|
||||
|
||||
export default function register(app) {
|
||||
app.get("/asset/endpoint", {
|
||||
async handler() {
|
||||
return ASSET_API_ENDPOINT;
|
||||
},
|
||||
});
|
||||
}
|
5
identity-api/src/routes/asset/index.js
Normal file
5
identity-api/src/routes/asset/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import endpoint from "./endpoint.js";
|
||||
|
||||
export default function registerRoutes(app, auth) {
|
||||
endpoint(app, auth);
|
||||
}
|
21
identity-api/src/routes/auth/account.js
Normal file
21
identity-api/src/routes/auth/account.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
export default function register(app, auth) {
|
||||
app.get("/auth/account", {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
if (payload.uid == null) {
|
||||
reply.status(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
user.password = undefined;
|
||||
user.entries = undefined;
|
||||
return user;
|
||||
},
|
||||
schema: {
|
||||
headers: { $ref: "schema://identity/authorization" },
|
||||
},
|
||||
});
|
||||
}
|
17
identity-api/src/routes/auth/genkey.js
Normal file
17
identity-api/src/routes/auth/genkey.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
export default function register(app, auth) {
|
||||
app.get("/auth/genkey", {
|
||||
async handler(request) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let session_key = await auth.createSessionKey(payload.uid);
|
||||
|
||||
return {
|
||||
session_key,
|
||||
};
|
||||
},
|
||||
schema: {
|
||||
headers: { $ref: "schema://identity/authorization" },
|
||||
},
|
||||
});
|
||||
}
|
24
identity-api/src/routes/auth/heirs.js
Normal file
24
identity-api/src/routes/auth/heirs.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
export default function register(app, auth) {
|
||||
app.post("/auth/heirs", {
|
||||
async handler(request, reply) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
if (payload.uid == null) {
|
||||
reply.status(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
user.heirs = request.body;
|
||||
|
||||
await auth.updateUser(payload.uid, user);
|
||||
},
|
||||
schema: {
|
||||
headers: { $ref: "schema://identity/authorization" },
|
||||
body: {
|
||||
type: "array",
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
13
identity-api/src/routes/auth/index.js
Normal file
13
identity-api/src/routes/auth/index.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import account from "./account.js";
|
||||
import genkey from "./genkey.js";
|
||||
import heirs from "./heirs.js";
|
||||
import login from "./login.js";
|
||||
import register from "./register.js";
|
||||
|
||||
export default function registerRoutes(app, auth) {
|
||||
account(app, auth);
|
||||
genkey(app, auth);
|
||||
heirs(app, auth);
|
||||
login(app, auth);
|
||||
register(app, auth);
|
||||
}
|
30
identity-api/src/routes/auth/login.js
Normal file
30
identity-api/src/routes/auth/login.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
export default function register(app, auth) {
|
||||
app.post("/auth/login", {
|
||||
async handler(request, reply) {
|
||||
let user = await auth.findUserByEmail(request.body.email);
|
||||
|
||||
if (user != null && user.password == request.body.password) {
|
||||
let token = await auth.createJwt(user.uid);
|
||||
|
||||
return {
|
||||
token,
|
||||
};
|
||||
}
|
||||
|
||||
reply.code(400);
|
||||
return {
|
||||
error: "invalid credentials",
|
||||
};
|
||||
},
|
||||
schema: {
|
||||
body: {
|
||||
type: "object",
|
||||
properties: {
|
||||
email: { type: "string" },
|
||||
password: { type: "string" },
|
||||
},
|
||||
},
|
||||
required: ["email", "password"],
|
||||
},
|
||||
});
|
||||
}
|
36
identity-api/src/routes/auth/register.js
Normal file
36
identity-api/src/routes/auth/register.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
export default function register(app, auth) {
|
||||
app.post("/auth/register", {
|
||||
async handler(request, reply) {
|
||||
if ((await auth.findUserByEmail(request.body.email)) == null) {
|
||||
let user = await auth.addUser({
|
||||
email: request.body.email,
|
||||
password: request.body.password,
|
||||
name: request.body.name,
|
||||
assets: [],
|
||||
limits: {
|
||||
assetCount: 5,
|
||||
},
|
||||
entries: [],
|
||||
});
|
||||
|
||||
return { token: await auth.createJwt(user.uid) };
|
||||
}
|
||||
|
||||
reply.code(400);
|
||||
return {
|
||||
error: "invalid data",
|
||||
};
|
||||
},
|
||||
schema: {
|
||||
body: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
email: { type: "string" },
|
||||
password: { type: "string" },
|
||||
},
|
||||
required: ["name", "email", "password"],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
51
identity-api/src/routes/entry/index.js
Normal file
51
identity-api/src/routes/entry/index.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { randomUUID } from "node:crypto";
|
||||
import list from "./list.js";
|
||||
|
||||
export default function registerRoutes(app, auth) {
|
||||
list(app, auth);
|
||||
|
||||
app.delete("/entry", {
|
||||
async handler(request) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
user.entries = user.entries.filter((v) => v.id !== request.query.entry_id);
|
||||
|
||||
await auth.updateUser(payload.uid, user);
|
||||
},
|
||||
schema: {
|
||||
headers: { $ref: "schema://identity/authorization" },
|
||||
query: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entry_id: { type: "string" },
|
||||
},
|
||||
required: ["entry_id"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app.put("/entry", {
|
||||
async handler(request) {
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
request.body.entry.id = randomUUID().toString();
|
||||
user.entries = [request.body.entry, ...user.entries];
|
||||
|
||||
await auth.updateUser(payload.uid, user);
|
||||
},
|
||||
schema: {
|
||||
headers: { $ref: "schema://identity/authorization" },
|
||||
body: {
|
||||
type: "object",
|
||||
properties: {
|
||||
entry: { type: "object" },
|
||||
},
|
||||
required: ["entry"],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
27
identity-api/src/routes/entry/list.js
Normal file
27
identity-api/src/routes/entry/list.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
export default function register(app, auth) {
|
||||
app.get("/entry/list", {
|
||||
async handler(request, reply) {
|
||||
if (request.query.offset < 0 || request.query.limit <= 0) {
|
||||
reply.status(400);
|
||||
return [];
|
||||
}
|
||||
|
||||
let jwt = request.headers["authorization"].replace("Bearer", "").trim();
|
||||
let { payload } = await auth.verifyJwt(jwt);
|
||||
|
||||
let user = await auth.user(payload.uid);
|
||||
return user.entries.slice(request.query.offset, request.query.offset + request.query.limit);
|
||||
},
|
||||
schema: {
|
||||
headers: { $ref: "schema://identity/authorization" },
|
||||
query: {
|
||||
type: "object",
|
||||
properties: {
|
||||
limit: { type: "number" },
|
||||
offset: { type: "number" },
|
||||
},
|
||||
required: ["limit", "offset"],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
11
identity-api/src/routes/index.js
Normal file
11
identity-api/src/routes/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import m2m from "./m2m/index.js";
|
||||
import asset from "./asset/index.js";
|
||||
import entry from "./entry/index.js";
|
||||
import authRoutes from "./auth/index.js";
|
||||
|
||||
export function registerRoutes(app, auth) {
|
||||
m2m(app, auth);
|
||||
asset(app, auth);
|
||||
entry(app, auth);
|
||||
authRoutes(app, auth);
|
||||
}
|
20
identity-api/src/routes/m2m/account.js
Normal file
20
identity-api/src/routes/m2m/account.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { contentFromSigned, verifySignature } from "../../m2m.js";
|
||||
|
||||
export default function register(app, auth) {
|
||||
app.post("/m2m/account", {
|
||||
async handler(request, reply) {
|
||||
if (!verifySignature(request.body)) {
|
||||
reply.statusCode(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let body = JSON.parse(contentFromSigned(request.body));
|
||||
|
||||
let user = await auth.findUserBySessionKey(body.session_key);
|
||||
user.password = undefined;
|
||||
user.entries = undefined;
|
||||
|
||||
return user;
|
||||
},
|
||||
});
|
||||
}
|
20
identity-api/src/routes/m2m/asset.js
Normal file
20
identity-api/src/routes/m2m/asset.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { contentFromSigned, verifySignature } from "../../m2m.js";
|
||||
|
||||
export default function register(app, auth) {
|
||||
app.put("/m2m/asset", {
|
||||
async handler(request, reply) {
|
||||
if (!verifySignature(request.body)) {
|
||||
reply.statusCode(401);
|
||||
return;
|
||||
}
|
||||
|
||||
let body = JSON.parse(contentFromSigned(request.body));
|
||||
|
||||
let user = await auth.findUserBySessionKey(body.session_key);
|
||||
user.assets.push(body.asset_id);
|
||||
|
||||
await auth.updateUser(user.uid, user);
|
||||
app.log.info((await auth.findUserBySessionKey(body.session_key)).assets);
|
||||
},
|
||||
});
|
||||
}
|
7
identity-api/src/routes/m2m/index.js
Normal file
7
identity-api/src/routes/m2m/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import asset from "./asset.js";
|
||||
import account from "./account.js";
|
||||
|
||||
export default function registerRoutes(app, auth) {
|
||||
asset(app, auth);
|
||||
account(app, auth);
|
||||
}
|
Loading…
Reference in a new issue