mirror of
https://gitea.smigz.com/smiggiddy/odin-codeprojects.git
synced 2025-06-27 11:20:40 -04:00
more updates and changes
This commit is contained in:
parent
269d7784ab
commit
8be819bf91
15 changed files with 426 additions and 98 deletions
133
.dockerignore
Normal file
133
.dockerignore
Normal file
|
@ -0,0 +1,133 @@
|
|||
*.db
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
.DS_STORE
|
|
@ -31,6 +31,7 @@ model File {
|
|||
size Int
|
||||
mimetype String
|
||||
createdAt DateTime @default(now())
|
||||
modifiedAt DateTime @default("2020-03-19T14:21:00+02:00")
|
||||
owner User? @relation(fields: [ownerId], references: [id])
|
||||
ownerId String?
|
||||
folder Folder? @relation(fields: [folderId], references: [id])
|
||||
|
|
|
@ -24,6 +24,11 @@ body {
|
|||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 6. Improve media defaults */
|
||||
img,
|
||||
picture,
|
||||
|
@ -134,9 +139,19 @@ input[type='file']:focus::file-selector-button {
|
|||
}
|
||||
}
|
||||
|
||||
/* .file { */
|
||||
/* opacity: 0; */
|
||||
/* width: 0.1px; */
|
||||
/* height: 0.1px; */
|
||||
/* position: absolute; */
|
||||
/* } */
|
||||
main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.directory-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
const multer = require('multer');
|
||||
const Db = require('../models/db');
|
||||
const {
|
||||
uploadFile,
|
||||
uploadToStorage,
|
||||
mkDirectory,
|
||||
getDirectoryContents,
|
||||
getProperPath,
|
||||
createFileRecord,
|
||||
getFileMetaData,
|
||||
editFile,
|
||||
} = require('../services/fileService');
|
||||
// const storage = multer.memoryStorage();
|
||||
// const upload = multer({ storage: storage });
|
||||
const { getFullUploadPath, formatBytes } = require('../utils/formatHelpers');
|
||||
const { Prisma } = require('@prisma/client');
|
||||
const upload = multer({ dest: '/tmp/odin/' });
|
||||
|
||||
const db = new Db();
|
||||
|
||||
const createDirectory = async (req, res) => {
|
||||
const { parentId, 'directory-name': directoryName } = req.body;
|
||||
const referer = req.get('referer');
|
||||
|
@ -28,12 +27,15 @@ const fileUpload = async (req, res) => {
|
|||
const { folderId, folderName } = req.body;
|
||||
const referer = req.get('referer');
|
||||
|
||||
let fullPath = await getProperPath(folderId);
|
||||
fullPath =
|
||||
fullPath === '/' ? `/${folderName}` : fullPath + `/${folderName}`;
|
||||
try {
|
||||
const path = await getFullUploadPath(
|
||||
folderId,
|
||||
folderName,
|
||||
req.user.username,
|
||||
req.file.originalname,
|
||||
);
|
||||
|
||||
const path = `${req.user.username}/${fullPath}/${req.file.originalname}`;
|
||||
const file = {
|
||||
const fileMetaData = {
|
||||
name: req.file.originalname,
|
||||
path: path,
|
||||
folderId: folderId,
|
||||
|
@ -46,17 +48,24 @@ const fileUpload = async (req, res) => {
|
|||
data: req.file.filename,
|
||||
};
|
||||
|
||||
try {
|
||||
const supabaseResult = await uploadFile(data);
|
||||
if (supabaseResult.error === null) {
|
||||
const result = await db.file.createFile(req.user.id, file);
|
||||
console.log(result);
|
||||
const supabaseResult = await uploadToStorage(data);
|
||||
if (!supabaseResult.error) {
|
||||
// make sure the uploaded file and the db have the same ID
|
||||
fileMetaData.id = supabaseResult.data.id;
|
||||
const result = await createFileRecord(req.user.id, fileMetaData);
|
||||
if (result instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
// if user uploads the samefile name, just overwrite it
|
||||
await editFile(fileMetaData);
|
||||
}
|
||||
res.redirect(referer);
|
||||
} else {
|
||||
console.error(supabaseResult.error);
|
||||
res.status(500).redirect(referer);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
res.status(500).redirect(referer);
|
||||
}
|
||||
|
||||
res.redirect(referer);
|
||||
};
|
||||
|
||||
const directoryContents = async (req, res) => {
|
||||
|
@ -74,6 +83,31 @@ const directoryContents = async (req, res) => {
|
|||
}
|
||||
};
|
||||
|
||||
const getFiles = (req, res) => {};
|
||||
const getFileData = async (req, res) => {
|
||||
const { fileId } = req.query;
|
||||
|
||||
module.exports = { createDirectory, upload, fileUpload, directoryContents };
|
||||
try {
|
||||
const file = await getFileMetaData(fileId);
|
||||
file.size = formatBytes(file.size);
|
||||
|
||||
res.render('partials/fileInfo', {
|
||||
file: file,
|
||||
pageTitle: 'FileUpload - File Details',
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
res.redirect('/');
|
||||
}
|
||||
};
|
||||
|
||||
const editFilePost = async (req, res) => {
|
||||
const { id, name, url, modifiedAt, mimetype } = req.body;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createDirectory,
|
||||
upload,
|
||||
fileUpload,
|
||||
directoryContents,
|
||||
getFileData,
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ passport.deserializeUser(async (id, done) => {
|
|||
});
|
||||
|
||||
const loggedIn = function (req, res, next) {
|
||||
if (req.isAuthenticated()) console.log('logged in');
|
||||
if (!req.user) res.redirect('/');
|
||||
next();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { PrismaClient } = require('@prisma/client');
|
||||
const { Prisma, PrismaClient } = require('@prisma/client');
|
||||
|
||||
class File {
|
||||
constructor() {
|
||||
|
@ -87,8 +87,6 @@ class File {
|
|||
}
|
||||
}
|
||||
|
||||
async getDirectoriesByUser() {}
|
||||
|
||||
async createFile(userId, file) {
|
||||
try {
|
||||
const result = await this.prisma.file.create({
|
||||
|
@ -99,6 +97,48 @@ class File {
|
|||
url: file.path,
|
||||
folderId: file.folderId,
|
||||
ownerId: userId,
|
||||
id: file.id,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
async editFile(file) {
|
||||
const data = await this.prisma.file.update({
|
||||
where: { id: file.id },
|
||||
data: {
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
mimetype: file.mimetype,
|
||||
url: file.path,
|
||||
modifiedAt: new Date(),
|
||||
},
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteFile(userId, fileId) {
|
||||
try {
|
||||
const result = await this.prisma.file.delete({
|
||||
data: {
|
||||
id: fileId,
|
||||
ownerId: userId,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
async getFileMetaData(fileId) {
|
||||
try {
|
||||
const result = await this.prisma.file.findUnique({
|
||||
where: {
|
||||
id: fileId,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
|
|
|
@ -11,5 +11,6 @@ fileRouter.post(
|
|||
);
|
||||
fileRouter.post('/directory', loggedIn, fileController.createDirectory);
|
||||
fileRouter.get('/directory', loggedIn, fileController.directoryContents);
|
||||
fileRouter.get('/file', loggedIn, fileController.getFileData);
|
||||
|
||||
module.exports = fileRouter;
|
||||
|
|
|
@ -8,6 +8,14 @@ const supabase = createClient(supabaseUrl, supabaseKey);
|
|||
|
||||
const db = new Db();
|
||||
|
||||
async function createFileRecord(userId, fileData) {
|
||||
return await db.file.createFile(userId, fileData);
|
||||
}
|
||||
|
||||
async function editFile(file) {
|
||||
return await db.file.editFile(file);
|
||||
}
|
||||
|
||||
async function getDirectoryContents(directoryId, userId) {
|
||||
return await db.file.getDirectoryContents(directoryId, userId);
|
||||
}
|
||||
|
@ -16,10 +24,10 @@ async function getParentDirectories(directoryId) {
|
|||
return await db.file.getParentFolders(directoryId);
|
||||
}
|
||||
|
||||
async function uploadFile(file) {
|
||||
async function uploadToStorage(file) {
|
||||
const { data, error } = await supabase.storage
|
||||
.from('odin')
|
||||
.upload(file.path, file.data);
|
||||
.upload(file.path, file.data, { upsert: true });
|
||||
|
||||
return { data: data, error: error };
|
||||
}
|
||||
|
@ -52,11 +60,18 @@ async function getProperPath(directoryId) {
|
|||
return directories.join('/');
|
||||
}
|
||||
|
||||
async function getFileMetaData(fileId) {
|
||||
return await db.file.getFileMetaData(fileId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
uploadFile,
|
||||
createFileRecord,
|
||||
editFile,
|
||||
uploadToStorage,
|
||||
mkDirectory,
|
||||
getParentDirectories,
|
||||
getDirectoryContents,
|
||||
getProperPath,
|
||||
rmDirectory,
|
||||
getFileMetaData,
|
||||
};
|
||||
|
|
32
file-uploader/src/utils/formatHelpers.js
Normal file
32
file-uploader/src/utils/formatHelpers.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
const { getProperPath } = require('../services/fileService');
|
||||
|
||||
const getFullUploadPath = async (folderId, folderName, username, filename) => {
|
||||
let fullPath = await getProperPath(folderId);
|
||||
fullPath = fullPath === '/' ? `${folderName}` : fullPath + `/${folderName}`;
|
||||
|
||||
return `${username}/${fullPath}/${filename}`;
|
||||
};
|
||||
|
||||
const formatBytes = (bytes, decimals = 2) => {
|
||||
if (!+bytes) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = [
|
||||
'Bytes',
|
||||
'KiB',
|
||||
'MiB',
|
||||
'GiB',
|
||||
'TiB',
|
||||
'PiB',
|
||||
'EiB',
|
||||
'ZiB',
|
||||
'YiB',
|
||||
];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
||||
};
|
||||
|
||||
module.exports = { formatBytes, getFullUploadPath };
|
|
@ -1,12 +1,25 @@
|
|||
<%- include('partials/header') %>
|
||||
<body>
|
||||
<% if(currentUser) { %>
|
||||
<div class="container flex">
|
||||
<p> Hello <%= currentUser.username %> </p>
|
||||
<a href="/auth/logout">sign out</a>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
<div class="container flex">
|
||||
<%- include('partials/newDirectory') %>
|
||||
<%- include('partials/fileUpload') %>
|
||||
</div>
|
||||
|
||||
|
||||
<%- include('partials/parentDirectories') %>
|
||||
<%- include('partials/directoryListing') %>
|
||||
</div>
|
||||
</main>
|
||||
<% } else { %>
|
||||
<main>
|
||||
<form action="/auth/login" method="post">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username">
|
||||
|
@ -15,6 +28,7 @@
|
|||
<button type="submit">Submit</button>
|
||||
<a href="/auth/register">Click here to register</a>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
|
||||
<% } %>
|
||||
|
|
|
@ -1,12 +1,41 @@
|
|||
<div class="container">
|
||||
<h2> Folders</h2>
|
||||
<ul>
|
||||
<% directoryListing.Directories.forEach(d => { %>
|
||||
<li><a href="/fs/<%= currentUser.username %>/<%= d.id %>"> <%= d.name %> </a></li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
<h2> Files: </h2>
|
||||
<% directoryListing.File.forEach(f => { %>
|
||||
<p> <%= f.name %> <%= f.id %></p>
|
||||
<% }); %>
|
||||
</div>
|
||||
<section class="container directory-listing">
|
||||
<div class="directory-grid">
|
||||
<div class="col file-name heading directory__grid_heading">
|
||||
<p>Name</p>
|
||||
</div>
|
||||
|
||||
<div class="col file-size directory__grid__heading">
|
||||
<p>Size</p>
|
||||
</div>
|
||||
|
||||
<div class="col file-modified directory__grid__heading">
|
||||
<p>Modified Last</p>
|
||||
</div>
|
||||
<!-- Combined loop for both directories and files -->
|
||||
<%
|
||||
const renderItem = (item, isDirectory) => {
|
||||
%>
|
||||
<div class="col file-name">
|
||||
<% if (isDirectory) { %>
|
||||
d <a href="/fs/<%= currentUser.username %>/<%= item.id %>"><%= item.name %></a> /
|
||||
<% } else { %>
|
||||
- <%= item.name %> <a href="/file/file?fileId=<%= item.id %>">Details</a>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="col file-size">
|
||||
<p><%= item?.size %></p>
|
||||
</div>
|
||||
<div class="col file-modified">
|
||||
<p><%= item?.modifiedAt %></p>
|
||||
</div>
|
||||
<% }; %>
|
||||
|
||||
<!-- Render directories -->
|
||||
<% directoryListing.Directories.forEach(d => renderItem(d, true)); %>
|
||||
|
||||
<!-- Render files -->
|
||||
<% directoryListing.File.forEach(f => renderItem(f, false)); %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
|
10
file-uploader/src/views/partials/fileInfo.ejs
Normal file
10
file-uploader/src/views/partials/fileInfo.ejs
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div class="container">
|
||||
|
||||
<div class="file-metadata">
|
||||
<h2>File: <%= file.name %> </h2>
|
||||
<p>Size: <%= file.size %> </p>
|
||||
<p>Created At: <%= file.createdAt %></p>
|
||||
<p>Modified At: <%= file?.modifiedAt %></p>
|
||||
</div>
|
||||
<a href="/fs/<%= currentUser.username %>/<%= file.folderId %>">Back</a>
|
||||
</div>
|
|
@ -1,18 +1,12 @@
|
|||
<form action="/file" method="post" enctype="multipart/form-data">
|
||||
<div class="container file-upload">
|
||||
<section class="file-upload">
|
||||
<h2 class="h2-heading">New upload</h2>
|
||||
<form action="/file" method="post" enctype="multipart/form-data">
|
||||
<div class="container file-upload">
|
||||
<input type="file" name="fileUpload" id="fileUpload" class="file" required>
|
||||
<input type="hidden" name="folderId" value=<%= folder.id %>>
|
||||
<input type="hidden" name="folderName" value=<%= folder.name %>>
|
||||
<button type="submit">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<form action="/file/directory" method="post">
|
||||
<div class="container">
|
||||
<label for="directory-name">Directory Name:</label>
|
||||
<input type="text" name="directory-name" placeholder="" required>
|
||||
<input type="hidden" name="parentId" value=<%= folder.id %>>
|
||||
<button>Create Directory</button>
|
||||
<button type="submit" class="btn">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
|
10
file-uploader/src/views/partials/newDirectory.ejs
Normal file
10
file-uploader/src/views/partials/newDirectory.ejs
Normal file
|
@ -0,0 +1,10 @@
|
|||
<form action="/file/directory" method="post">
|
||||
<div class="container">
|
||||
<div class="form-item">
|
||||
<label for="directory-name">Directory Name:</label>
|
||||
<input type="text" name="directory-name" placeholder="" required>
|
||||
</div>
|
||||
<input type="hidden" name="parentId" value=<%= folder.id %>>
|
||||
<button class="btn">Create Directory</button>
|
||||
</div>
|
||||
</form>
|
|
@ -1,9 +1,8 @@
|
|||
<div class="container">
|
||||
<p>
|
||||
<p class="directory-path">
|
||||
<% parentDirectories.forEach(d => { %>
|
||||
<a href="/fs/<%= currentUser.username %>/<%= d.id %>"><%= d.name %></a><span> /</span>
|
||||
|
||||
<% }); %>
|
||||
<span> <%= directoryListing.name %></span>
|
||||
<span class="current-directory"> <%= directoryListing.name %></span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue