db integration and scripts
This commit is contained in:
parent
4577bb72b9
commit
31ce135b3a
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
config.json
|
config.json
|
||||||
dist/
|
dist/
|
||||||
|
*.sqlite
|
@ -5,7 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"tailwind": "npx tailwindcss -i ./src/client/public/css/main.css -o ./src/client/public/css/tailwind.css",
|
"tailwind": "npx tailwindcss -i ./src/client/public/css/main.css -o ./src/client/public/css/tailwind.css",
|
||||||
"build": "./build.sh",
|
"build": "./scripts/build.sh",
|
||||||
"dev": "nodemon -r tsconfig-paths/register ./src/app.ts",
|
"dev": "nodemon -r tsconfig-paths/register ./src/app.ts",
|
||||||
"start": "node dist/app.js"
|
"start": "node dist/app.js"
|
||||||
},
|
},
|
||||||
@ -28,10 +28,12 @@
|
|||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-session": "^1.18.1",
|
"express-session": "^1.18.1",
|
||||||
"jquery": "^3.7.1",
|
"jquery": "^3.7.1",
|
||||||
|
"knex": "^3.1.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-discord": "^0.1.4",
|
"passport-discord": "^0.1.4",
|
||||||
"preline": "^2.7.0",
|
"preline": "^2.7.0",
|
||||||
|
"sqlite3": "^5.1.7",
|
||||||
"tsconfig-paths": "^4.2.0"
|
"tsconfig-paths": "^4.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
cd "$(dirname "$0")/../"
|
||||||
|
|
||||||
echo "Erasing previous dist folder"
|
echo "Erasing previous dist folder"
|
||||||
rm -r ./dist
|
rm -rf ./dist
|
||||||
|
|
||||||
echo "Compiling tailwind css ..."
|
echo "Compiling tailwind css ..."
|
||||||
npx tailwindcss -i ./src/client/public/css/main.css -o ./src/client/public/css/tailwind.css
|
npx tailwindcss -i ./src/client/public/css/main.css -o ./src/client/public/css/tailwind.css
|
||||||
@ -7,6 +9,9 @@ npx tailwindcss -i ./src/client/public/css/main.css -o ./src/client/public/css/t
|
|||||||
echo "Compiling typescript ..."
|
echo "Compiling typescript ..."
|
||||||
npx tsc --project ./tsconfig.json
|
npx tsc --project ./tsconfig.json
|
||||||
|
|
||||||
|
echo "Copying client files"
|
||||||
|
cp -r src/client dist/client
|
||||||
|
|
||||||
echo "Building typescript path aliases ..."
|
echo "Building typescript path aliases ..."
|
||||||
npx tsc-alias -p ./tsconfig.json
|
npx tsc-alias -p ./tsconfig.json
|
||||||
|
|
2
scripts/migrate.sh
Executable file
2
scripts/migrate.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
cd "$(dirname "$0")/../"
|
||||||
|
npx knex migrate:latest --knexfile ./src/knexfile.ts
|
2
scripts/seeds.sh
Executable file
2
scripts/seeds.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
cd "$(dirname "$0")/../"
|
||||||
|
npx knex seed:run --knexfile ./src/knexfile.ts
|
26
src/app.ts
26
src/app.ts
@ -1,3 +1,4 @@
|
|||||||
|
import path from "path";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import engine from "ejs-mate";
|
import engine from "ejs-mate";
|
||||||
import passport from "passport";
|
import passport from "passport";
|
||||||
@ -7,28 +8,29 @@ import dotenv from "dotenv";
|
|||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
import "@bot/bot";
|
import "@bot/bot";
|
||||||
import { setupPassport } from "@server/controllers/auth";
|
import { setupPassport } from "@server/controllers/auth.web.controller";
|
||||||
import { attachUser } from "@server/middleware/attachUser";
|
import { attachUser } from "@server/middleware/attachUser";
|
||||||
import { flashMiddleware } from "@server/middleware/flash";
|
import { flashMiddleware } from "@server/middleware/flash";
|
||||||
import { ensureAuthenticated } from "@server/middleware/authenticated";
|
import { ensureAuthenticated } from "@server/middleware/authenticated";
|
||||||
|
|
||||||
// import routers & middleware
|
// import routers & middleware
|
||||||
import { attachGuilds } from "@server/middleware/attachGuilds";
|
import { attachGuilds } from "@server/middleware/attachGuilds";
|
||||||
import { router as homeRouter } from "@server/routes/home";
|
import homeWebRouter from "@server/routes/home.web.routes";
|
||||||
import { router as guildRouter } from "@server/routes/guild";
|
import guildApiRouter from "@server/routes/guild.api.routes";
|
||||||
import { router as authRouter } from "@server/routes/auth";
|
import guildWebRouter from "@server/routes/guild.web.routes";
|
||||||
|
import authWebRouter from "@server/routes/auth.web.routes";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.engine("ejs", engine);
|
app.engine("ejs", engine);
|
||||||
app.set("views", "./src/client/views");
|
app.set("views", path.resolve(__dirname, "client/views"));
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
|
|
||||||
// Public files, including 3rd party resources (foreign)
|
// Public files, including 3rd party resources (foreign)
|
||||||
app.use("/static", express.static("./src/client/public"));
|
app.use("/static", express.static(path.resolve(__dirname, "client/public")));
|
||||||
app.use("/static/foreign/preline.js", express.static("./node_modules/preline/dist/preline.js"));
|
app.use("/static/foreign/preline.js", express.static(path.resolve(__dirname, "../node_modules/preline/dist/preline.js")));
|
||||||
app.use("/static/foreign/jquery.js", express.static("./node_modules/jquery/dist/jquery.min.js"));
|
app.use("/static/foreign/jquery.js", express.static(path.resolve(__dirname, "../node_modules/jquery/dist/jquery.min.js")));
|
||||||
app.use("/static/foreign/dataTables.js", express.static("./node_modules/datatables.net-dt/js/dataTables.dataTables.min.js"));
|
app.use("/static/foreign/dataTables.js", express.static(path.resolve(__dirname, "../node_modules/datatables.net/js/dataTables.min.js")));
|
||||||
|
|
||||||
// User authentication & sessions
|
// User authentication & sessions
|
||||||
app.use(session({
|
app.use(session({
|
||||||
@ -45,9 +47,9 @@ app.use(flash());
|
|||||||
app.use(flashMiddleware);
|
app.use(flashMiddleware);
|
||||||
|
|
||||||
// register routers & middleware
|
// register routers & middleware
|
||||||
app.use("/auth", authRouter);
|
app.use("/auth", authWebRouter);
|
||||||
app.use("/guild", ensureAuthenticated, attachUser, attachGuilds, guildRouter);
|
app.use("/guild", ensureAuthenticated, attachUser, attachGuilds, guildWebRouter, guildApiRouter);
|
||||||
app.use("/", ensureAuthenticated, attachUser, attachGuilds, homeRouter);
|
app.use("/", ensureAuthenticated, attachUser, attachGuilds, homeWebRouter);
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ client.on("ready", () => {
|
|||||||
throw Error("Client is null");
|
throw Error("Client is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
client.user.setActivity("Set Activity", { type: ActivityType.Watching });
|
client.user.setActivity("new sources", { type: ActivityType.Watching });
|
||||||
console.log(`Discord Bot '${client.user.displayName}' is online!`)
|
console.log(`Discord Bot '${client.user.displayName}' is online!`)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -918,6 +918,10 @@ select {
|
|||||||
margin-inline-start: auto !important;
|
margin-inline-start: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.-me-0\.5 {
|
||||||
|
margin-inline-end: -0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
.-mt-px {
|
.-mt-px {
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
}
|
}
|
||||||
@ -1151,10 +1155,6 @@ select {
|
|||||||
width: 18rem;
|
width: 18rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-\[100rem\] {
|
|
||||||
width: 100rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-\[260px\] {
|
.w-\[260px\] {
|
||||||
width: 260px;
|
width: 260px;
|
||||||
}
|
}
|
||||||
@ -1543,6 +1543,11 @@ select {
|
|||||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-yellow-100 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(254 249 195 / var(--tw-bg-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.bg-opacity-50 {
|
.bg-opacity-50 {
|
||||||
--tw-bg-opacity: 0.5;
|
--tw-bg-opacity: 0.5;
|
||||||
}
|
}
|
||||||
@ -1872,6 +1877,11 @@ select {
|
|||||||
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
color: rgb(255 255 255 / var(--tw-text-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-yellow-800 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(133 77 14 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.opacity-0 {
|
.opacity-0 {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
@ -2562,6 +2572,26 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dt-ordering-asc .hs-datatable-ordering-asc\:text-blue-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-ordering-asc.hs-datatable-ordering-asc\:text-blue-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-ordering-desc .hs-datatable-ordering-desc\:text-blue-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-ordering-desc.hs-datatable-ordering-desc\:text-blue-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.complete .hs-file-upload-complete\:bg-green-600 {
|
.complete .hs-file-upload-complete\:bg-green-600 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(22 163 74 / var(--tw-bg-opacity, 1));
|
background-color: rgb(22 163 74 / var(--tw-bg-opacity, 1));
|
||||||
@ -2830,6 +2860,10 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
|
|||||||
background-color: rgb(20 184 166 / 0.1);
|
background-color: rgb(20 184 166 / 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark\:bg-yellow-500\/10:where(.dark, .dark *) {
|
||||||
|
background-color: rgb(234 179 8 / 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.dark\:bg-opacity-80:where(.dark, .dark *) {
|
.dark\:bg-opacity-80:where(.dark, .dark *) {
|
||||||
--tw-bg-opacity: 0.8;
|
--tw-bg-opacity: 0.8;
|
||||||
}
|
}
|
||||||
@ -2900,6 +2934,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
|
|||||||
color: rgb(255 255 255 / 0.6);
|
color: rgb(255 255 255 / 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark\:text-yellow-500:where(.dark, .dark *) {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(234 179 8 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.dark\:placeholder-neutral-500:where(.dark, .dark *)::-moz-placeholder {
|
.dark\:placeholder-neutral-500:where(.dark, .dark *)::-moz-placeholder {
|
||||||
--tw-placeholder-opacity: 1;
|
--tw-placeholder-opacity: 1;
|
||||||
color: rgb(115 115 115 / var(--tw-placeholder-opacity, 1));
|
color: rgb(115 115 115 / var(--tw-placeholder-opacity, 1));
|
||||||
@ -2994,6 +3033,26 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
|
|||||||
--tw-ring-offset-color: #1f2937;
|
--tw-ring-offset-color: #1f2937;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dt-ordering-asc .dark\:hs-datatable-ordering-asc\:text-blue-500:where(.dark, .dark *) {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-ordering-asc.dark\:hs-datatable-ordering-asc\:text-blue-500:where(.dark, .dark *) {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-ordering-desc .dark\:hs-datatable-ordering-desc\:text-blue-500:where(.dark, .dark *) {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-ordering-desc.dark\:hs-datatable-ordering-desc\:text-blue-500:where(.dark, .dark *) {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(59 130 246 / var(--tw-text-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
.\[\&\:\:-webkit-scrollbar-thumb\]\:rounded-full::-webkit-scrollbar-thumb {
|
.\[\&\:\:-webkit-scrollbar-thumb\]\:rounded-full::-webkit-scrollbar-thumb {
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
}
|
}
|
||||||
|
18
src/client/public/js/guild/subscriptions.js
Normal file
18
src/client/public/js/guild/subscriptions.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
// Filter by status
|
||||||
|
(function () {
|
||||||
|
const radioButtons = document.querySelectorAll('input[type="radio"][name="filter"]');
|
||||||
|
const { dataTable } = new HSDataTable('#table');
|
||||||
|
|
||||||
|
dataTable.search.fixed('status', function (searchStr, data, index) {
|
||||||
|
const status = data[2].trim().toLowerCase(); // Adjust index based on your dataset
|
||||||
|
|
||||||
|
if (radioButtons[0].checked && status === 'active') return true;
|
||||||
|
if (radioButtons[1].checked && status === 'inactive') return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
radioButtons.forEach(radio => {
|
||||||
|
radio.addEventListener('change', () => dataTable.draw());
|
||||||
|
});
|
||||||
|
})();
|
@ -1,5 +1,5 @@
|
|||||||
<nav class="bg-white dark:bg-neutral-900 border-b dark:border-none">
|
<nav class="bg-white dark:bg-neutral-900 border-b dark:border-none">
|
||||||
<div class="max-w-[100rem] w-full mx-auto sm:flex sm:flex-row sm:justify-between sm:items-center sm:gap-x-3 py-3 sm:py-5 px-4 sm:px-6 lg:px-8">
|
<div class="w-full mx-auto sm:flex sm:flex-row sm:justify-between sm:items-center sm:gap-x-3 py-3 sm:py-5 px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex justify-between items-center gap-x-3">
|
<div class="flex justify-between items-center gap-x-3">
|
||||||
<div class="grow flex items-center gap-x-4">
|
<div class="grow flex items-center gap-x-4">
|
||||||
<% if (guild.icon) { %>
|
<% if (guild.icon) { %>
|
||||||
|
@ -3,14 +3,19 @@
|
|||||||
<%- include("guildHeader") -%>
|
<%- include("guildHeader") -%>
|
||||||
|
|
||||||
<!-- Table Section -->
|
<!-- Table Section -->
|
||||||
<div class="max-w-full w-[100rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto overflow-hidden">
|
<div id="table" class="max-w-full overflow-hidden p-4 sm:p-6"> <!-- px-4 py-10 sm:px-6 lg:px-8 lg:py-14 -->
|
||||||
<!-- Card -->
|
<!-- Card -->
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col" data-hs-datatable='{
|
||||||
<div class="-m-1.5 "> <!-- overflow-x-auto -->
|
"selecting": true,
|
||||||
|
"rowSelectingOptions": {
|
||||||
|
"selectAllSelector": "#selectAllBox"
|
||||||
|
}
|
||||||
|
}'>
|
||||||
|
<div class="-m-1.5">
|
||||||
<div class="max-w-full p-1.5 min-w-full inline-block align-middle">
|
<div class="max-w-full p-1.5 min-w-full inline-block align-middle">
|
||||||
<div class="bg-white border border-gray-200 rounded-md shadow-sm overflow-hidden dark:bg-neutral-900 dark:border-neutral-700">
|
<div class="bg-white border border-gray-200 rounded-md shadow-sm overflow-hidden dark:bg-neutral-900 dark:border-neutral-700">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="px-6 py-4 gap-3 flex flex-nowrap justify-between items-center border-b border-gray-200 dark:border-neutral-700">
|
<div class="px-6 py-4 gap-3 flex flex-nowrap justify-between items-center border-gray-200 dark:border-neutral-700">
|
||||||
<!-- Input -->
|
<!-- Input -->
|
||||||
<div class="hidden sm:block sm:col-span-1">
|
<div class="hidden sm:block sm:col-span-1">
|
||||||
<label for="hs-as-table-product-review-search" class="sr-only">Search</label>
|
<label for="hs-as-table-product-review-search" class="sr-only">Search</label>
|
||||||
@ -78,66 +83,94 @@
|
|||||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-neutral-700">
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-neutral-700">
|
||||||
<thead class="bg-gray-50 dark:bg-neutral-800">
|
<thead class="bg-gray-50 dark:bg-neutral-800">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="ps-6 py-3 text-start">
|
<th scope="col" class="ps-6 py-3 text-start --exclude-from-ordering">
|
||||||
<label for="hs-at-with-checkboxes-main" class="flex ml-[1px]">
|
<label for="hs-at-with-checkboxes-main" class="flex ml-[1px]">
|
||||||
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-main">
|
<input type="checkbox" id="selectAllBox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800">
|
||||||
<span class="sr-only">Checkbox</span>
|
<span class="sr-only">Checkbox</span>
|
||||||
</label>
|
</label>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
||||||
Name
|
Name
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
||||||
URL
|
URL
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
||||||
Channels
|
Channels
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
||||||
Filters
|
Filters
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
||||||
Style
|
Style
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200 text-nowrap">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200 text-nowrap">
|
||||||
Created at
|
Created at
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-6 py-3 text-start">
|
<th scope="col" class="px-6 py-3 text-start">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
|
||||||
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
|
||||||
Status
|
Status
|
||||||
</span>
|
</span>
|
||||||
|
<svg class="size-3.5 ms-1 -me-0.5 text-gray-400 dark:text-neutral-500" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path class="hs-datatable-ordering-desc:text-blue-600 dark:hs-datatable-ordering-desc:text-blue-500" d="m7 15 5 5 5-5"></path>
|
||||||
|
<path class="hs-datatable-ordering-asc:text-blue-600 dark:hs-datatable-ordering-asc:text-blue-500" d="m7 9 5-5 5 5"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -151,7 +184,7 @@
|
|||||||
<td class="size-px whitespace-nowrap">
|
<td class="size-px whitespace-nowrap">
|
||||||
<div class="ps-6 py-3">
|
<div class="ps-6 py-3">
|
||||||
<label for="hs-at-with-checkboxes-1" class="flex">
|
<label for="hs-at-with-checkboxes-1" class="flex">
|
||||||
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-1">
|
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-1" data-hs-datatable-row-selecting-individual="">
|
||||||
<span class="sr-only">Checkbox</span>
|
<span class="sr-only">Checkbox</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -206,8 +239,8 @@
|
|||||||
|
|
||||||
<td class="size-px whitespace-nowrap">
|
<td class="size-px whitespace-nowrap">
|
||||||
<div class="ps-6 py-3">
|
<div class="ps-6 py-3">
|
||||||
<label for="hs-at-with-checkboxes-1" class="flex">
|
<label for="hs-at-with-checkboxes-2" class="flex">
|
||||||
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-1">
|
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-2" data-hs-datatable-row-selecting-individual="">
|
||||||
<span class="sr-only">Checkbox</span>
|
<span class="sr-only">Checkbox</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -215,13 +248,13 @@
|
|||||||
|
|
||||||
<td class="size-px whitespace-nowrap align-top">
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
||||||
BBC News · Top Stories
|
Sky News
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="size-px whitespace-nowrap align-top">
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
||||||
https://bbci.co.uk/feeds/news/rss.xml
|
https://sky.co.uk/feeds/news/rss.xml
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -262,8 +295,8 @@
|
|||||||
|
|
||||||
<td class="size-px whitespace-nowrap">
|
<td class="size-px whitespace-nowrap">
|
||||||
<div class="ps-6 py-3">
|
<div class="ps-6 py-3">
|
||||||
<label for="hs-at-with-checkboxes-1" class="flex">
|
<label for="hs-at-with-checkboxes-3" class="flex">
|
||||||
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-1">
|
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-3" data-hs-datatable-row-selecting-individual="">
|
||||||
<span class="sr-only">Checkbox</span>
|
<span class="sr-only">Checkbox</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -271,13 +304,13 @@
|
|||||||
|
|
||||||
<td class="size-px whitespace-nowrap align-top">
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
||||||
BBC News · Top Stories
|
Fox News
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="size-px whitespace-nowrap align-top">
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
||||||
https://bbci.co.uk/feeds/news/rss.xml
|
https://fox.com/feeds/news/rss.xml
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -313,6 +346,62 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr class="bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800">
|
||||||
|
|
||||||
|
<td class="size-px whitespace-nowrap">
|
||||||
|
<div class="ps-6 py-3">
|
||||||
|
<label for="hs-at-with-checkboxes-1" class="flex">
|
||||||
|
<input type="checkbox" class="shrink-0 border-gray-300 rounded text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-at-with-checkboxes-1" data-hs-datatable-row-selecting-individual="">
|
||||||
|
<span class="sr-only">Checkbox</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
|
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
||||||
|
News Agency 40
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
|
<a href="#" class="block p-6 text-blue-500 hover:text-blue-600 focus:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500">
|
||||||
|
https://news.co.uk/feeds/news/rss.xml
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
|
<div class="p-6">
|
||||||
|
<span class="text-sm text-gray-500 dark:text-neutral-500">
|
||||||
|
30th, Jan 2025
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
|
<div class="p-6">
|
||||||
|
<span class="py-1 px-1.5 inline-flex items-center gap-x-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded-full dark:bg-yellow-500/10 dark:text-yellow-500">
|
||||||
|
<svg class="size-2.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"></path>
|
||||||
|
</svg>
|
||||||
|
Warning
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
<!-- <tr class="bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800">
|
<!-- <tr class="bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800">
|
||||||
<td class="size-px whitespace-nowrap align-top">
|
<td class="size-px whitespace-nowrap align-top">
|
||||||
@ -424,4 +513,6 @@
|
|||||||
<!-- End Card -->
|
<!-- End Card -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- End Table Section -->
|
<!-- End Table Section -->
|
||||||
|
|
||||||
|
<% block("scripts").append('<script src="/static/js/guild/subscriptions.js"></script>'); %>
|
@ -16,9 +16,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- End Content -->
|
<!-- End Content -->
|
||||||
|
|
||||||
<script src="/static/foreign/preline.js"></script>
|
|
||||||
<script src="/static/foreign/jquery.js"></script>
|
<script src="/static/foreign/jquery.js"></script>
|
||||||
<script src="/static/foreign/dataTables.js"></script>
|
<script src="/static/foreign/dataTables.js"></script>
|
||||||
|
<script src="/static/foreign/preline.js"></script>
|
||||||
<script src="/static/js/main.js"></script>
|
<script src="/static/js/main.js"></script>
|
||||||
|
<%- block("scripts").toString() %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
4
src/db/db.ts
Normal file
4
src/db/db.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import knex from "knex";
|
||||||
|
import knexConfig from "@server/../knexfile";
|
||||||
|
|
||||||
|
export const db = knex(knexConfig);
|
@ -0,0 +1,19 @@
|
|||||||
|
import type { Knex } from "knex";
|
||||||
|
|
||||||
|
const TABLE = "subscriptions";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.createTable(TABLE, table => {
|
||||||
|
table.increments("id").primary();
|
||||||
|
table.string("name").notNullable();
|
||||||
|
table.string("url").notNullable();
|
||||||
|
table.string("guild_id").notNullable();
|
||||||
|
table.boolean("active").notNullable().defaultTo(true);
|
||||||
|
table.timestamps(true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TABLE);
|
||||||
|
}
|
||||||
|
|
11
src/db/models/subs.model.ts
Normal file
11
src/db/models/subs.model.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Url } from "url";
|
||||||
|
|
||||||
|
export interface Subscription {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
url: Url;
|
||||||
|
guild_id: string;
|
||||||
|
active: boolean;
|
||||||
|
created_at: Date;
|
||||||
|
updated_at: Date;
|
||||||
|
}
|
17
src/db/seeds/test_sub.ts
Normal file
17
src/db/seeds/test_sub.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
const TABLE = "subscriptions";
|
||||||
|
|
||||||
|
export async function seed(knex: Knex): Promise<void> {
|
||||||
|
// Deletes ALL existing entries
|
||||||
|
await knex(TABLE).del();
|
||||||
|
|
||||||
|
// Inserts seed entries
|
||||||
|
await knex(TABLE).insert([
|
||||||
|
{ name: "My First Subscription", url: "https://bbci.co.uk/feeds/news.xml", guild_id: "899773845223927878", active: true },
|
||||||
|
{ name: "My Second Sub", url: "https://bbci.co.uk/feeds/news.xml", guild_id: "899773845223927878", active: true },
|
||||||
|
{ name: "My Third Sub", url: "https://bbci.co.uk/feeds/news.xml", guild_id: "1204426362794811453", active: true },
|
||||||
|
{ name: "My Fourth Sub", url: "https://bbci.co.uk/feeds/news.xml", guild_id: "899773845223927878", active: true },
|
||||||
|
{ name: "My Fith Sub", url: "https://bbci.co.uk/feeds/news.xml", guild_id: "1204426362794811453", active: true },
|
||||||
|
]);
|
||||||
|
};
|
47
src/knexfile.ts
Normal file
47
src/knexfile.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import path from "path";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
dotenv.config(); // I env vars are already loaded, but this file is invoked directly sometimes.
|
||||||
|
|
||||||
|
const {
|
||||||
|
DB_CLIENT = "sqlite", // Default:
|
||||||
|
SQLITE_FILE = path.resolve(__dirname, "../db.sqlite"), // use sqlite if not specified
|
||||||
|
PG_HOST = "",
|
||||||
|
PG_PORT = "",
|
||||||
|
PG_USER = "",
|
||||||
|
PG_PASSWORD = "",
|
||||||
|
PG_DATABASE = ""
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
const dbConfig = {
|
||||||
|
sqlite: {
|
||||||
|
client: "sqlite3",
|
||||||
|
connection: {
|
||||||
|
filename: SQLITE_FILE
|
||||||
|
},
|
||||||
|
useNullAsDefault: true
|
||||||
|
},
|
||||||
|
postgresql: {
|
||||||
|
client: "pg",
|
||||||
|
connection: {
|
||||||
|
host: PG_HOST,
|
||||||
|
port: PG_PORT,
|
||||||
|
user: PG_USER,
|
||||||
|
password: PG_PASSWORD,
|
||||||
|
database: PG_DATABASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const knexConfig = {
|
||||||
|
...dbConfig[DB_CLIENT as keyof typeof dbConfig],
|
||||||
|
migrations: {
|
||||||
|
tableName: "knex_migrations",
|
||||||
|
directory: path.resolve(__dirname, "./db/migrations")
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: path.resolve(__dirname, "./db/seeds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default knexConfig;
|
@ -50,3 +50,5 @@ export const setupPassport = (passport: PassportStatic) => {
|
|||||||
done(null, user);
|
done(null, user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default { get, authenticate, logout };
|
@ -14,4 +14,6 @@ export const get = async (request: Request, response: Response) => {
|
|||||||
title: `${guild.name} - Relay`,
|
title: `${guild.name} - Relay`,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default { get };
|
@ -14,4 +14,6 @@ export const get = async (request: Request, response: Response) => {
|
|||||||
title: `${guild.name} - Relay`,
|
title: `${guild.name} - Relay`,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default { get };
|
@ -14,4 +14,6 @@ export const get = async (request: Request, response: Response) => {
|
|||||||
title: `${guild.name} - Relay`,
|
title: `${guild.name} - Relay`,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default { get };
|
@ -14,4 +14,6 @@ export const get = async (request: Request, response: Response) => {
|
|||||||
title: `${guild.name} - Relay`,
|
title: `${guild.name} - Relay`,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default { get };
|
29
src/server/controllers/guild/sub.api.controller.ts
Normal file
29
src/server/controllers/guild/sub.api.controller.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Request, Response } from "express";
|
||||||
|
import { Subscription } from "@db/models/subs.model";
|
||||||
|
import { db } from "@db/db";
|
||||||
|
|
||||||
|
export const get = async (request: Request, response: Response) => {
|
||||||
|
try {
|
||||||
|
const subscriptions = await db<Subscription>("subscriptions")
|
||||||
|
.select("*")
|
||||||
|
.where({ guild_id: request.params.guildId });
|
||||||
|
|
||||||
|
response.json(subscriptions);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
response.status(500).json({ error: "Failed to fetch subscriptions" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const post = async (request: Request, response: Response) => {
|
||||||
|
try {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
response.status(500).json({ error: "Failed to create subscription" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { get, post }
|
@ -14,4 +14,6 @@ export const get = async (request: Request, response: Response) => {
|
|||||||
title: `${guild.name} - Relay`,
|
title: `${guild.name} - Relay`,
|
||||||
guild: guild,
|
guild: guild,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export default { get }
|
@ -5,3 +5,5 @@ export const get = async (_request: Request, response: Response) => {
|
|||||||
title: "Dashboard - Relay"
|
title: "Dashboard - Relay"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default { get };
|
@ -1,34 +0,0 @@
|
|||||||
const knex = require("knex");
|
|
||||||
|
|
||||||
const {
|
|
||||||
DB_CLIENT,
|
|
||||||
SQLITE_FILE,
|
|
||||||
PG_HOST,
|
|
||||||
PG_PORT,
|
|
||||||
PG_USER,
|
|
||||||
PG_PASSWORD,
|
|
||||||
PG_DATABASE
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
const dbConfig = {
|
|
||||||
sqlite: {
|
|
||||||
client: "sqlite3",
|
|
||||||
connection: {
|
|
||||||
filename: SQLITE_FILE
|
|
||||||
},
|
|
||||||
useNullAsDefault: true
|
|
||||||
},
|
|
||||||
postgresql: {
|
|
||||||
client: "pg",
|
|
||||||
connection: {
|
|
||||||
host: PG_HOST,
|
|
||||||
port: PG_PORT,
|
|
||||||
user: PG_USER,
|
|
||||||
password: PG_PASSWORD,
|
|
||||||
database: PG_DATABASE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const db = knex(dbConfig[DB_CLIENT]);
|
|
||||||
module.exports = db;
|
|
@ -1,9 +1,11 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import { ensureAuthenticated, forwardAuthenticated } from "@server/middleware/authenticated";
|
import { ensureAuthenticated, forwardAuthenticated } from "@server/middleware/authenticated";
|
||||||
import * as controller from "@server/controllers/auth";
|
import controller from "@server/controllers/auth.web.controller";
|
||||||
|
|
||||||
export const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/login", forwardAuthenticated, controller.get);
|
router.get("/login", forwardAuthenticated, controller.get);
|
||||||
router.get("/logout", ensureAuthenticated, controller.logout);
|
router.get("/logout", ensureAuthenticated, controller.logout);
|
||||||
router.get("/api", forwardAuthenticated, controller.authenticate);
|
router.get("/api", forwardAuthenticated, controller.authenticate);
|
||||||
|
|
||||||
|
export default router;
|
9
src/server/routes/guild.api.routes.ts
Normal file
9
src/server/routes/guild.api.routes.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
import subApiController from "@server/controllers/guild/sub.api.controller";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
router.get("/:guildId/subscriptions/api", subApiController.get);
|
||||||
|
router.post("/:guildId/subscriptions/api", subApiController.post);
|
||||||
|
|
||||||
|
export default router;
|
@ -1,15 +0,0 @@
|
|||||||
import { Router } from "express";
|
|
||||||
import * as overviewController from "@server/controllers/guild/overview";
|
|
||||||
import * as subscriptionController from "@server/controllers/guild/subscriptions";
|
|
||||||
import * as filterController from "@server/controllers/guild/filters";
|
|
||||||
import * as styleController from "@server/controllers/guild/styles";
|
|
||||||
import * as contentController from "@server/controllers/guild/content";
|
|
||||||
import { getGuildPage } from "@server/middleware/guildPage";
|
|
||||||
|
|
||||||
export const router = Router();
|
|
||||||
|
|
||||||
router.get("/:guildId", getGuildPage, overviewController.get);
|
|
||||||
router.get("/:guildId/subscriptions", getGuildPage, subscriptionController.get);
|
|
||||||
router.get("/:guildId/filters", getGuildPage, filterController.get);
|
|
||||||
router.get("/:guildId/styles", getGuildPage, styleController.get);
|
|
||||||
router.get("/:guildId/content", getGuildPage, contentController.get);
|
|
17
src/server/routes/guild.web.routes.ts
Normal file
17
src/server/routes/guild.web.routes.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
import { getGuildPage } from "@server/middleware/guildPage";
|
||||||
|
import indexWebController from "@server/controllers/guild/index.web.controller";
|
||||||
|
import subWebController from "@server/controllers/guild/sub.web.controller";
|
||||||
|
import filterWebController from "@server/controllers/guild/filter.web.controller";
|
||||||
|
import styleWebController from "@server/controllers/guild/style.web.controller";
|
||||||
|
import contentWebController from "@server/controllers/guild/content.web.controller";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
router.get("/:guildId", getGuildPage, indexWebController.get);
|
||||||
|
router.get("/:guildId/subscriptions", getGuildPage, subWebController.get);
|
||||||
|
router.get("/:guildId/filters", getGuildPage, filterWebController.get);
|
||||||
|
router.get("/:guildId/style", getGuildPage, styleWebController.get);
|
||||||
|
router.get("/:guildId/content", getGuildPage, contentWebController.get);
|
||||||
|
|
||||||
|
export default router;
|
@ -1,6 +0,0 @@
|
|||||||
import { Router } from "express";
|
|
||||||
import * as controller from "@server/controllers/home";
|
|
||||||
|
|
||||||
export const router = Router();
|
|
||||||
|
|
||||||
router.get("/", controller.get);
|
|
8
src/server/routes/home.web.routes.ts
Normal file
8
src/server/routes/home.web.routes.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import controller from "@server/controllers/home.web.controller";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/", controller.get);
|
||||||
|
|
||||||
|
export default router;
|
@ -30,13 +30,11 @@
|
|||||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
"@/*": ["src/*"],
|
"@db/*": ["src/db/*"],
|
||||||
"@server/*": ["src/server/*"],
|
"@server/*": ["src/server/*"],
|
||||||
"@client/*": ["src/client/*"],
|
"@client/*": ["src/client/*"],
|
||||||
"@bot/*": ["src/bot/*"],
|
"@bot/*": ["src/bot/*"],
|
||||||
"@utils/*": ["src/utils/*"],
|
"@utils/*": ["src/utils/*"],
|
||||||
"@views/*": ["src/client/views/*"],
|
|
||||||
"@public/*": ["src/client/public/*"],
|
|
||||||
"@node_modules/*": ["node_modules/*"],
|
"@node_modules/*": ["node_modules/*"],
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user