Skip to content
This repository was archived by the owner on Aug 16, 2024. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9f35e12
Bump submodules
bperel Dec 4, 2023
373e0ee
WIP
bperel Feb 6, 2024
08909fc
WIP
bperel Feb 7, 2024
8c86d00
WIP
bperel Feb 9, 2024
663e3db
WIP
bperel Feb 10, 2024
f25e165
WIP
bperel Feb 10, 2024
23bb523
WIP
bperel Feb 10, 2024
40ad227
WIP
bperel Feb 12, 2024
7ef4914
WIP
bperel Feb 12, 2024
82ffd75
WIP
bperel Feb 13, 2024
a28a028
Init new UI
bperel Feb 16, 2024
dcf73db
WIP
bperel Feb 17, 2024
094a9d1
Init Redis cache for Cloudinary
bperel Feb 17, 2024
3da7401
WIP
bperel Feb 18, 2024
53f698b
WIP redis to DB
bperel Feb 19, 2024
e7863ed
WIP
bperel Feb 25, 2024
3158314
WIP
bperel Feb 25, 2024
5b63bb7
WIP
bperel Feb 25, 2024
c35c5cc
WIP
bperel Feb 26, 2024
0a1f97f
Update dependencies
bperel Feb 26, 2024
a4d312d
WIP
bperel Feb 27, 2024
0af2a1e
WIP
bperel Feb 28, 2024
b10a160
Cleanup dependencies
bperel Feb 29, 2024
af49d37
Init entry resize
bperel Feb 29, 2024
abe59e0
WIP
bperel Mar 2, 2024
e42b705
WIP
bperel Mar 2, 2024
cd48bfe
Fix and apply eslint
bperel Mar 3, 2024
28ceea2
WIP
bperel Mar 3, 2024
c54919e
Bump dependencies
bperel Mar 27, 2024
2f63237
Bump dependencies
bperel Mar 31, 2024
176bd0c
Bump dependencies
bperel Apr 6, 2024
b48f2ae
Cleanup
bperel Apr 7, 2024
9037138
Fix translation
bperel Apr 21, 2024
ccd5975
Bump dependencies
bperel Apr 24, 2024
eb8c4f9
Bump dependencies
bperel May 8, 2024
48a15c0
Bump dependencies
bperel May 10, 2024
2033ccc
WIP cleanup socket usage
bperel May 13, 2024
936fc94
Cleanup
bperel May 14, 2024
a18cd93
Cleanup paths
bperel May 20, 2024
74b1a8b
Update dependencies
bperel May 20, 2024
84951e1
Cleanup socket injection
bperel May 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
VITE_CLOUDINARY_CLOUDNAME=dl7hskxab
VITE_FRONTEND_URL=http://localhost:8004
VITE_DM_URL=http://localhost:8001
VITE_DM_API_URL=http://localhost:3000
VITE_DM_SOCKET_URL=http://localhost:3000
VITE_DUMILI_SOCKET_URL=http://localhost:3002
VITE_FLAGS_ROOT=https://ducksmanager.net/images/flags/
VITE_IMAGES_ROOT=/images/
VITE_BACKEND_URL=http://localhost:3002
5 changes: 2 additions & 3 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
VITE_FRONTEND_URL=https://dumili.ducksmanager.net
VITE_DM_URL=https://ducksmanager.net
VITE_DM_API_URL=https://api-2.ducksmanager.net
VITE_DM_URL=https://socket.ducksmanager.net
VITE_DM_SOCKET_URL=https://socket.api.ducksmanager.net
VITE_FLAGS_ROOT=https://ducksmanager.net/images/flags/
VITE_BACKEND_URL=https://dumili-api.ducksmanager.net
44 changes: 21 additions & 23 deletions .eslintrc.js → .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
module.exports = {
extends: [
{
"extends": [
// add more generic rulesets here, such as:
// 'eslint:recommended',
"plugin:vue/vue3-recommended",
"plugin:prettier-vue/recommended",
"prettier",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended"
],
parser: "vue-eslint-parser",
parserOptions: { parser: "@typescript-eslint/parser" },
overrides: [
"parser": "vue-eslint-parser",
"parserOptions": { "parser": "@typescript-eslint/parser" },
"overrides": [
{
files: ["*.js", "*.ts", "*.vue"],
},
"files": ["*.ts", "*.vue"]
}
],
root: true,
rules: {
"root": true,
"rules": {
"@typescript-eslint/no-non-null-assertion": "off",
"simple-import-sort/exports": "error",
"simple-import-sort/imports": "error",
Expand All @@ -32,18 +32,16 @@ module.exports = {
"error",
"kebab-case",
{
registeredComponentsOnly: true,
ignores: [],
},
],
"registeredComponentsOnly": true,
"ignores": []
}
]
},
plugins: ["simple-import-sort", "@typescript-eslint", "unused-imports"],
ignorePatterns: [
"**/node_modules",
"plugins": ["simple-import-sort", "@typescript-eslint", "unused-imports"],
"ignorePatterns": [
"../**",
"*.d.ts",
"**/dist",
"shims.d.ts",
"vendor",
"auto-imports.d.ts",
"component.d.ts",
],
};
"**/node_modules"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ dist-ssr
.eslintcache
auto-imports.d.ts
components.d.ts
api/prisma/client_*
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
..
1 change: 0 additions & 1 deletion .prettierrc.js

This file was deleted.

2 changes: 0 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ COPY {package.json,pnpm-*.yaml} /app/

WORKDIR /app/apps/dumili/api
COPY --from=build /app/apps/dumili/api/dist/api ./
COPY --from=build /app/packages/axios-helper/package.json ./packages/axios-helper/
COPY --from=build /app/packages/axios-helper/dist ./packages/axios-helper

COPY apps/dumili/api/package.json ./
RUN --mount=type=cache,id=pnpm-store-dumili,target=/app/.pnpm-store \
Expand Down
6 changes: 5 additions & 1 deletion api/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
KUMIKO_HOST=http://localhost:8005
OCR_HOST=http://localhost:8006
DM_SOCKET_URL=http://localhost:3000

CLOUDINARY_URL=cloudinary://my_key:my_secret@my_cloud_name
CLOUDINARY_URL=cloudinary://my_key:my_secret@my_cloud_name?secure=true

DATABASE_URL=mysql://root:changeme@localhost:64097/dumili
MYSQL_ROOT_PASSWORD=changeme

TOKEN_SECRET=3543c30fe79047b4f73cfb61aa1eb52cb3173de4b3941e0fc4ec1b127bbeed6019695a1a453a81c33c2eea964ccc577e69c7df994124bd2751e262a311ea23a1
25 changes: 25 additions & 0 deletions api/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": [
// add more generic rulesets here, such as:
"eslint:recommended",
"plugin:prettier-vue/recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"parserOptions": { "parser": "@typescript-eslint/parser" },
"overrides": [
{
"files": ["*.ts"]
}
],
"root": true,
"rules": {
"@typescript-eslint/no-non-null-assertion": "off",
"simple-import-sort/exports": "error",
"simple-import-sort/imports": "error",
"unused-imports/no-unused-imports": "error"
},
"plugins": ["simple-import-sort", "@typescript-eslint", "unused-imports"],
"ignorePatterns": ["**/node_modules", "**/dist",
"prisma/client_dumili"]
}
1 change: 1 addition & 0 deletions api/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
206 changes: 173 additions & 33 deletions api/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import * as Sentry from "@sentry/node";
import busboy from "connect-busboy";
import cors from "cors";
import busboy from "busboy";
import { v2 as cloudinary } from "cloudinary";
import dotenv from "dotenv";
import express from "express";
import { router } from "express-file-routing";
import fs from "fs";
import { createServer } from "http";
import path from "path";
import { fromBuffer } from "pdf2pic";
import { Namespace, Server } from "socket.io";
import { EventsMap } from "socket.io/dist/typed-events";

import { authenticateToken } from "~routes/_auth";
import { SessionUser } from "~dm-types/SessionUser";

import { PrismaClient } from "./prisma/client_dumili";
import { authenticateUser } from "./services/_auth";
import indexations from "./services/indexations";
import { FullIndexation } from "./services/indexations/types";

dotenv.config({
path: ".env",
Expand All @@ -16,34 +24,166 @@ dotenv.config({
override: true,
});

const port = 3002;
const [, API_KEY, API_SECRET, CLOUD_NAME] =
process.env.CLOUDINARY_URL?.match(/cloudinary:\/\/(\d+):(\w+)@(\w+)/) ?? [];
cloudinary.config({
cloud_name: CLOUD_NAME,
api_key: API_KEY,
api_secret: API_SECRET,
});

export type SessionDataWithIndexation = {
user: SessionUser;
indexation: FullIndexation;
};
export type SessionData = { user: SessionUser };
export class ServerWithData<Data extends object> extends Server<
Record<string, never>,
Record<string, never>,
Record<string, never>,
Data
> {}

export type NamespaceWithData<
Services extends EventsMap,
Data extends object = object,
> = Namespace<Services, Record<string, never>, Record<string, never>, Data>;

export const prisma = new PrismaClient();

Sentry.init({
dsn: process.env.SENTRY_DSN,
const httpServer = createServer(async (req, res) => {
if (req.url === "/upload") {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Request-Method", "*");
res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
res.setHeader("Access-Control-Allow-Headers", "*");
if (req.method === "OPTIONS") {
res.writeHead(204);
res.end();
} else {
const { headers } = req;
const indexationId = headers["x-dumili-indexation-id"] as string;
const token = headers["x-token"] as string;
authenticateUser(token)
.then((user) => {
const filename = "";
const bb = busboy({ headers });
bb.on("file", async (name, file, info) => {
let imagesToUpload: string[];
switch (info.mimeType) {
case "image/jpg":
case "image/jpeg":
case "image/png": {
const saveTo = path.join(__dirname, name);
file.pipe(fs.createWriteStream(saveTo));
imagesToUpload = [filename];
break;
}
case "application/pdf": {
const chunks = [];
for await (const chunk of file) {
chunks.push(chunk);
}
const buffer = Buffer.concat(chunks);
imagesToUpload = (
await fromBuffer(buffer, {
saveFilename: info.filename.replace(".pdf", ""),
}).bulk(-1)
).map(({ path }) => path!);
break;
}
default:
res.writeHead(400, { "Content-Type": "text/plain" });
res.write("Unsupported file type:" + info.mimeType);
return;
}

const firstNewPageNumber =
(
await prisma.page.findFirst({
where: {
indexationId: indexationId,
},
orderBy: {
pageNumber: "desc",
},
})
)?.pageNumber ?? 0;

const uploadedImages = (await Promise.all(
imagesToUpload.map(async (filename) =>
cloudinary.uploader
.upload(filename, {
upload_preset: "p1urov1k",
folder: `dumili/${user.username}/${indexationId}`,
})
.then(({ secure_url }) => secure_url)
.catch((e) => {
console.error(e);
res.writeHead(500, { "Content-Type": "text/plain" });
res.end(e);
})
.finally(() => {
fs.unlinkSync(filename);
}),
),
)) as string[];
const pagesToCreate = {
pages: {
create: uploadedImages.map((url, idx) => ({
pageNumber: firstNewPageNumber + idx + 1,
url,
})),
},
};

prisma.indexation
.upsert({
where: {
id: indexationId,
},
update: pagesToCreate,
create: {
dmUserId: user.id,
id: indexationId,
...pagesToCreate,
},
})
.then(() => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`upload success: ${filename}`);
})
.catch((e) => {
console.error(e);
res.writeHead(401, { "Content-Type": "text/plain" });
res.end(e);
});
});
bb.on("close", () => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`upload success: ${filename}`);
});
req.pipe(bb);
})
.catch((e) => {
console.error(e);
res.writeHead(401, { "Content-Type": "text/plain" });
res.end(e);
});
}
} else {
res.writeHead(404);
res.end();
}
});

const app = express();
app.use(
Sentry.Handlers.requestHandler({
user: ["id", "username"],
}) as express.RequestHandler
);
app.use(
cors({
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
})
);
app.use(busboy({ immediate: true }));

app.all(/^.+$/, authenticateToken);

app.use(express.json({ limit: "5mb" }));

router().then((r) => {
app.use("/", r);
app.use(Sentry.Handlers.errorHandler() as express.ErrorRequestHandler);

app.listen(port, () => {
console.log(`API listening on port ${port}`);
});
const io = new Server(httpServer, {
cors: {
origin: "*",
},
});

indexations(io);

httpServer.listen(3002);
console.log("Dumuli API open on port 3002");
6 changes: 0 additions & 6 deletions api/nodemon.json

This file was deleted.

Loading