in progress: configuring s3 to allow public get
This commit is contained in:
@@ -1,6 +1,37 @@
|
||||
import InProgress from "@/components/InProgress";
|
||||
import NotFound from "@/components/NotFound";
|
||||
import MusicController from "@/server/controllers/music.controller";
|
||||
import { MusicStreamingEntry } from "@/server/db/schema";
|
||||
import { S3Service } from "@/server/s3";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default async function ListenByCollectionID() {
|
||||
export default async function ListenByCollectionID({ params }: { params: { collectionid?: string }}) {
|
||||
const id = params.collectionid ? Number(params.collectionid) : undefined;
|
||||
if (!id) return <NotFound />
|
||||
|
||||
return <InProgress />
|
||||
const controller = new MusicController();
|
||||
const result = await controller.getByID(id);
|
||||
if (!result) return <NotFound />
|
||||
|
||||
console.log(result);
|
||||
const entries = await S3Service.getURLs(result.pathToEntry);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<header>
|
||||
<h1>{result.name}</h1>
|
||||
<p>{result.shortDescription}</p>
|
||||
</header>
|
||||
|
||||
<p>{result.longDescription}</p>
|
||||
|
||||
<section>
|
||||
{ entries
|
||||
? entries.map((entry: string, idx: number) => <p key={idx}>{entry}</p>)
|
||||
: <p>No entries found</p>
|
||||
}
|
||||
</section>
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
8
components/NotFound.tsx
Normal file
8
components/NotFound.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center w-screen h-screen">
|
||||
<h1 className="text-4xl font-bold">404</h1>
|
||||
<h2 className="text-2xl font-semibold">Page not found</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -12,11 +12,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.367.0",
|
||||
"@smithy/node-http-handler": "^2.1.8",
|
||||
"@t3-oss/env-nextjs": "^0.7.0",
|
||||
"@vercel/style-guide": "^5.0.1",
|
||||
"autoprefixer": "10.4.14",
|
||||
"ioredis": "^5.3.2",
|
||||
"next": "^13.4.12",
|
||||
"next": "^14.0.1",
|
||||
"pg": "^8.11.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { S3Client } from '@aws-sdk/client-s3';
|
||||
import Redis from 'ioredis';
|
||||
// import Redis from 'ioredis';
|
||||
import pg from 'pg';
|
||||
import { createDBClient } from '../db';
|
||||
import { Maybe, must } from '@/util/helpers';
|
||||
@@ -16,40 +16,47 @@ type ControllerOptions<T> = {
|
||||
|
||||
export default abstract class BaseController<T> {
|
||||
#db: pg.Client
|
||||
#bucket: S3Client
|
||||
#cache: Redis
|
||||
// #bucket: S3Client
|
||||
// #cache: Redis
|
||||
|
||||
tableName: string
|
||||
parser?: FullParserType<T>
|
||||
|
||||
constructor(options: ControllerOptions<T>) {
|
||||
this.#db = must(createDBClient);
|
||||
this.#bucket = must(createS3Client);
|
||||
this.#cache = must(createRedisClient);
|
||||
// this.#bucket = must(createS3Client);
|
||||
// this.#cache = must(createRedisClient);
|
||||
|
||||
this.tableName = options.tableName;
|
||||
this.parser = options.parser;
|
||||
}
|
||||
|
||||
async getAll(projection?: string): Promise<Maybe<T[]>> {
|
||||
'use server';
|
||||
try {
|
||||
// we'll enable cache here later
|
||||
await this.#db.connect();
|
||||
|
||||
const result = await this.#db.query("SELECT $1 FROM $2", [projection ?? "*", this.tableName]);
|
||||
return result.rows;
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
return null;
|
||||
} finally {
|
||||
await this.#db.end();
|
||||
}
|
||||
}
|
||||
|
||||
async getByID(id: number, projection?: string): Promise<Maybe<T>> {
|
||||
try {
|
||||
const result = await this.#db.query("SELECT $1 FROM $2 WHERE id = $3", [projection ?? "*", this.tableName, id]);
|
||||
await this.#db.connect();
|
||||
const result = await this.#db.query(`SELECT ${projection ?? "*"} FROM ${this.tableName} WHERE id = ${id}`);
|
||||
return result.rows[0];
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
return null;
|
||||
} finally {
|
||||
await this.#db.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ export const ZMusicStreamingEntry = z.object({
|
||||
name: z.string().max(100),
|
||||
shortDescription: z.string().max(100),
|
||||
longDescription: z.string().max(1000),
|
||||
pathToEntry: z.string(),
|
||||
tags: z.array(z.string().max(100)).optional(),
|
||||
pathToEntry: z.string().optional(),
|
||||
});
|
||||
|
||||
export type MusicStreamingEntry = z.infer<typeof ZMusicStreamingEntry>;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { S3Client, S3ClientConfig } from "@aws-sdk/client-s3";
|
||||
import { NodeHttpHandler } from "@smithy/node-http-handler";
|
||||
import https from "https";
|
||||
|
||||
export default function createS3Client() {
|
||||
if (typeof process.env.S3_ACCESS_KEY !== "string") {
|
||||
@@ -11,7 +13,17 @@ export default function createS3Client() {
|
||||
}
|
||||
|
||||
const config: S3ClientConfig = {
|
||||
endpoint: env.S3_ENDPOINT
|
||||
endpoint: env.S3_ENDPOINT,
|
||||
region: "us-east-1",
|
||||
|
||||
requestHandler: new NodeHttpHandler({
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
ciphers: "ALL",
|
||||
}),
|
||||
}),
|
||||
|
||||
forcePathStyle: true,
|
||||
}
|
||||
|
||||
if (env.S3_SECRET) {
|
||||
|
||||
@@ -2,8 +2,6 @@ import { ListObjectsV2Command, PutObjectCommand, PutObjectCommandOutput, S3Clien
|
||||
import { env } from "@/env.mjs";
|
||||
import createS3Client from "./createClient";
|
||||
import { Maybe, must } from "@/util/helpers";
|
||||
import { readFile } from "fs/promises";
|
||||
import { readFileSync, readdir } from "fs";
|
||||
|
||||
export default class S3Service {
|
||||
static async getFiles(key: string): Promise<Maybe<_Object[]>> {
|
||||
@@ -34,71 +32,4 @@ export default class S3Service {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async upload(filePath: string, key: string) {
|
||||
if (env.NODE_ENV != "development") {
|
||||
throw new Error("Cannot upload files in production");
|
||||
}
|
||||
|
||||
try {
|
||||
const client = must<S3Client>(createS3Client);
|
||||
const Body = await readFile(filePath);
|
||||
|
||||
const cmd = new PutObjectCommand({
|
||||
Bucket: env.S3_BUCKET,
|
||||
Key: key,
|
||||
Body,
|
||||
});
|
||||
|
||||
const result = await client.send(cmd);
|
||||
return result.$metadata.httpStatusCode == 200;
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async uploadAllInDirectory(dirPath: string, prefix: string): Promise<boolean> {
|
||||
if (env.NODE_ENV != "development") {
|
||||
throw new Error("Cannot upload files in production");
|
||||
}
|
||||
|
||||
try {
|
||||
const client = must<S3Client>(createS3Client);
|
||||
const promises = new Array<Promise<PutObjectCommandOutput>>();
|
||||
const results = new Array<boolean>();
|
||||
|
||||
readdir(dirPath, (err, files) => {
|
||||
if (err) {
|
||||
console.log({ err });
|
||||
return null;
|
||||
}
|
||||
|
||||
files.forEach(file => {
|
||||
const Key = `${prefix}/${file}`
|
||||
const Body = readFileSync(Key);
|
||||
|
||||
const cmd = new PutObjectCommand({
|
||||
Bucket: env.S3_BUCKET,
|
||||
Key,
|
||||
Body,
|
||||
})
|
||||
|
||||
promises.push(client.send(cmd));
|
||||
})
|
||||
})
|
||||
|
||||
promises.forEach(p => {
|
||||
setTimeout(async() => {
|
||||
const output = await Promise.resolve(p);
|
||||
results.push(output.$metadata.httpStatusCode == 200);
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
return results.every(r => true);
|
||||
} catch (error) {
|
||||
console.log({ error });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
server/sql/create_tables.sql
Normal file
20
server/sql/create_tables.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
CREATE TABLE IF NOT EXISTS music (
|
||||
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
name varchar(100),
|
||||
shortDescription varchar(100),
|
||||
longDescription varchar(1000),
|
||||
pathToEntry varchar
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
name varchar(100)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS music_tags (
|
||||
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
music_id INTEGER,
|
||||
tag_id INTEGER,
|
||||
FOREIGN KEY(music_id) REFERENCES music(id),
|
||||
FOREIGN KEY(tag_id) REFERENCES tags(id)
|
||||
);
|
||||
26
server/sql/seed.sql
Normal file
26
server/sql/seed.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
INSERT INTO music (name, shortDescription, longDescription, pathToEntry) VALUES (
|
||||
'Jisei',
|
||||
'My first major undertaking as a singer-songwriter',
|
||||
'Released in October 2020',
|
||||
'Jisei'
|
||||
);
|
||||
|
||||
INSERT INTO tags (name) VALUES
|
||||
(
|
||||
'indie-rock'
|
||||
),
|
||||
(
|
||||
'alternative'
|
||||
),
|
||||
(
|
||||
'guitar'
|
||||
),
|
||||
(
|
||||
'cello'
|
||||
);
|
||||
|
||||
INSERT INTO music_tags (music_id, tag_id) VALUES
|
||||
(1, 1),
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(1, 4);
|
||||
@@ -1,5 +1,5 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
export default {
|
||||
content: [
|
||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
|
||||
Reference in New Issue
Block a user