tags update
This commit is contained in:
parent
2de81ff1c1
commit
b3c19c24b6
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"latest": "4.10.6",
|
||||
"lastUpdateCheck": 1685050367579,
|
||||
"lastNotification": 1684550793542
|
||||
"latest": "4.11.1",
|
||||
"lastUpdateCheck": 1686705141258,
|
||||
"lastNotification": 1686696427587
|
||||
}
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
ironmouse "Thank you" (for testing): 4760169
|
||||
cj_clippy "Full library access" (for production): 9380584
|
||||
|
||||
cj_clippy "Your URL displayed on Futureporn.net": 10663202
|
||||
|
|
|
@ -1,4 +1,36 @@
|
|||
module.exports = ({ env }) => ({
|
||||
module.exports = ({
|
||||
env
|
||||
}) => ({
|
||||
'fuzzy-search': {
|
||||
enabled: true,
|
||||
config: {
|
||||
contentTypes: [{
|
||||
uid: 'api::tag.tag',
|
||||
modelName: 'tag',
|
||||
transliterate: false,
|
||||
queryConstraints: {
|
||||
where: {
|
||||
'$and': [
|
||||
{
|
||||
publishedAt: {
|
||||
'$notNull': true
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
fuzzysortOptions: {
|
||||
characterLimit: 32,
|
||||
threshold: -600,
|
||||
limit: 10,
|
||||
keys: [{
|
||||
name: 'name',
|
||||
weight: 100
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
config: {
|
||||
provider: 'cloudinary',
|
||||
|
@ -27,4 +59,4 @@ module.exports = ({ env }) => ({
|
|||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
// greets chatgpt
|
||||
async function getFileDetailsFromUrl(url) {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
const options = {
|
||||
signal,
|
||||
};
|
||||
|
||||
let retries = 10;
|
||||
|
||||
while (retries) {
|
||||
console.log(`fetching ${url}`);
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.log('fetch timed out, aborting...');
|
||||
controller.abort();
|
||||
}, 5000);
|
||||
|
||||
try {
|
||||
const res = await fetch(url, options);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
console.log('finished fetch');
|
||||
if (!res.ok) throw new Error(`problem while getting file from url with url ${url}`);
|
||||
if (!res?.headers?.get('x-bz-file-name')) throw new Error(`${url} did not have a x-bz-file-name in the response headers`);
|
||||
if (!res?.headers?.get('x-bz-file-id')) throw new Error(`${url} did not have a x-bz-file-id in the response headers`);
|
||||
|
||||
return {
|
||||
key: res.headers.get('x-bz-file-name'),
|
||||
url: url,
|
||||
uploadId: res.headers.get('x-bz-file-id'),
|
||||
};
|
||||
} catch (err) {
|
||||
clearTimeout(timeoutId);
|
||||
retries--;
|
||||
|
||||
if (retries === 0) {
|
||||
console.error(`Could not fetch file details from URL: ${url}.`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
console.warn(`Retrying fetch (${retries} attempts left)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
async up(knex) {
|
||||
// You have full access to the Knex.js API with an already initialized connection to the database
|
||||
|
||||
// Get all VODs from the database
|
||||
const vods = await knex.select('*').from('vods');
|
||||
|
||||
|
||||
// Process each VOD
|
||||
for (const vod of vods) {
|
||||
|
||||
// courtesy timer
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
|
||||
console.log(vod)
|
||||
// Get the file details from the VOD's video source URL
|
||||
if (vod?.video_src) {
|
||||
try {
|
||||
const fileDetails = await getFileDetailsFromUrl(vod.video_src);
|
||||
|
||||
// Insert the B2 file into the database
|
||||
const [file] = await knex('b2_files').insert({
|
||||
url: fileDetails.url,
|
||||
key: fileDetails.key,
|
||||
upload_id: fileDetails.uploadId,
|
||||
}).returning('id');
|
||||
|
||||
console.log(file)
|
||||
console.log(`attempting to insert vod_id:${vod.id}, b_2_file_id:${file.id} for videoSrcB2`)
|
||||
|
||||
// Link the B2 file to the VOD
|
||||
await knex('vods_video_src_b_2_links').insert({
|
||||
vod_id: vod.id,
|
||||
b_2_file_id: file.id,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.log(`there was an error so we are skipping vod ${vod.id}`)
|
||||
}
|
||||
} else {
|
||||
console.log(`${vod.id} has no video_src. skipping.`)
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
// const fetch = require('node-fetch')
|
||||
|
||||
// // greets chatgpt
|
||||
// async function getFileDetailsFromUrl(url) {
|
||||
// const controller = new AbortController();
|
||||
// const signal = controller.signal;
|
||||
|
||||
// const options = {
|
||||
// signal,
|
||||
// };
|
||||
|
||||
// let retries = 10;
|
||||
|
||||
// while (retries) {
|
||||
// console.log(`fetching ${url}`);
|
||||
// const timeoutId = setTimeout(() => {
|
||||
// console.log('fetch timed out, aborting...');
|
||||
// controller.abort();
|
||||
// }, 5000);
|
||||
|
||||
// try {
|
||||
// const res = await fetch(url, options);
|
||||
|
||||
// clearTimeout(timeoutId);
|
||||
|
||||
// console.log('finished fetch');
|
||||
// if (!res.ok) throw new Error(`problem while getting file from url with url ${url}`);
|
||||
// if (!res?.headers?.get('x-bz-file-name')) throw new Error(`${url} did not have a x-bz-file-name in the response headers`);
|
||||
// if (!res?.headers?.get('x-bz-file-id')) throw new Error(`${url} did not have a x-bz-file-id in the response headers`);
|
||||
|
||||
// return {
|
||||
// key: res.headers.get('x-bz-file-name'),
|
||||
// url: url,
|
||||
// uploadId: res.headers.get('x-bz-file-id'),
|
||||
// };
|
||||
// } catch (err) {
|
||||
// clearTimeout(timeoutId);
|
||||
// retries--;
|
||||
|
||||
// if (retries === 0) {
|
||||
// console.error(`Could not fetch file details from URL: ${url}.`);
|
||||
// throw err;
|
||||
// }
|
||||
|
||||
// console.warn(`Retrying fetch (${retries} attempts left)`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
async up(knex) {
|
||||
// You have full access to the Knex.js API with an already initialized connection to the database
|
||||
|
||||
|
||||
// we iterate through the local, non-strapi backup db first.
|
||||
// get list of all tags
|
||||
// for each tag
|
||||
// * get list of related vods
|
||||
// * create relation in Strapi
|
||||
// *
|
||||
|
||||
|
||||
|
||||
// Get all VODs from the database
|
||||
const vods = await knex.select('*').from('vods');
|
||||
|
||||
|
||||
// Process each VOD
|
||||
for (const vod of vods) {
|
||||
|
||||
// courtesy timer
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
|
||||
// @todo
|
||||
|
||||
console.log(vod)
|
||||
// Get the file details from the VOD's video source URL
|
||||
if (vod?.video_src) {
|
||||
try {
|
||||
const fileDetails = await getFileDetailsFromUrl(vod.video_src);
|
||||
|
||||
// Insert the B2 file into the database
|
||||
const [file] = await knex('b2_files').insert({
|
||||
url: fileDetails.url,
|
||||
key: fileDetails.key,
|
||||
upload_id: fileDetails.uploadId,
|
||||
}).returning('id');
|
||||
|
||||
console.log(file)
|
||||
console.log(`attempting to insert vod_id:${vod.id}, b_2_file_id:${file.id} for videoSrcB2`)
|
||||
|
||||
// Link the B2 file to the VOD
|
||||
await knex('vods_video_src_b_2_links').insert({
|
||||
vod_id: vod.id,
|
||||
b_2_file_id: file.id,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.log(`there was an error so we are skipping vod ${vod.id}`)
|
||||
}
|
||||
} else {
|
||||
console.log(`${vod.id} has no video_src. skipping.`)
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
|
@ -4,6 +4,26 @@ const { Client } = require('pg')
|
|||
const fetch = require('node-fetch')
|
||||
const _ = require('lodash');
|
||||
|
||||
|
||||
|
||||
// module.exports = {
|
||||
// async up(knex) {
|
||||
|
||||
// // Get all VODs from the database
|
||||
// const vods = await knex.select('*').from('vods');
|
||||
|
||||
// // sanity check every B2 URL
|
||||
// for (const vod of vods) {
|
||||
// await checkUrl(vod.video_src)
|
||||
// }
|
||||
|
||||
// console.log(`there are ${problemUrls.length} the problem urls`)
|
||||
// console.log(problemUrls)
|
||||
|
||||
// process.exit(5923423)
|
||||
// },
|
||||
// };
|
||||
|
||||
// const slugify = require('slugify')
|
||||
|
||||
|
||||
|
@ -110,6 +130,9 @@ async function main () {
|
|||
// get list of vods from our source db
|
||||
const vodsResponse = await client.query('SELECT tags, date, "announceUrl" FROM vod')
|
||||
|
||||
console.log(JSON.stringify(vodsResponse.rows))
|
||||
process.exit(5)
|
||||
|
||||
for (const vod of vodsResponse.rows) {
|
||||
|
||||
|
|
@ -22,7 +22,8 @@
|
|||
"@strapi/strapi": "4.9.0",
|
||||
"@strapi/utils": "^4.9.0",
|
||||
"better-sqlite3": "8.0.1",
|
||||
"pg": "^8.10.0"
|
||||
"pg": "^8.10.0",
|
||||
"strapi-plugin-fuzzy-search": "^1.11.0-beta.1"
|
||||
},
|
||||
"author": {
|
||||
"name": "CJ_Clippy"
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"kind": "collectionType",
|
||||
"collectionName": "issues",
|
||||
"info": {
|
||||
"singularName": "issue",
|
||||
"pluralName": "issues",
|
||||
"displayName": "issue",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": false
|
||||
},
|
||||
"pluginOptions": {},
|
||||
"attributes": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"sla": {
|
||||
"type": "enumeration",
|
||||
"enum": [
|
||||
"public",
|
||||
"patron",
|
||||
"authenticated"
|
||||
],
|
||||
"default": "public",
|
||||
"required": true
|
||||
},
|
||||
"type": {
|
||||
"type": "enumeration",
|
||||
"enum": [
|
||||
"stall"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* issue controller
|
||||
*/
|
||||
|
||||
const { createCoreController } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreController('api::issue.issue');
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* issue router
|
||||
*/
|
||||
|
||||
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreRouter('api::issue.issue');
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* issue service
|
||||
*/
|
||||
|
||||
const { createCoreService } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreService('api::issue.issue');
|
|
@ -1,5 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const { JWT } = require('@mux/mux-node');
|
||||
|
||||
const MUX_SIGNING_KEY_ID = process.env.MUX_SIGNING_KEY_ID;
|
||||
const MUX_SIGNING_KEY_PRIVATE_KEY = process.env.MUX_SIGNING_KEY_PRIVATE_KEY;
|
||||
const MUX_PLAYBACK_RESTRICTION_ID = process.env.MUX_PLAYBACK_RESTRICTION_ID
|
||||
|
@ -25,12 +27,25 @@ module.exports = createCoreController('api::mux-asset.mux-asset', ({ strapi }) =
|
|||
return
|
||||
}
|
||||
|
||||
ctx.body = await strapi.service('api::mux-asset.mux-asset').signJwt(
|
||||
ctx.query.id,
|
||||
MUX_SIGNING_KEY_ID,
|
||||
MUX_SIGNING_KEY_PRIVATE_KEY,
|
||||
MUX_PLAYBACK_RESTRICTION_ID
|
||||
)
|
||||
const tokens = {}
|
||||
|
||||
tokens.playbackToken = JWT.signPlaybackId(ctx.query.id, {
|
||||
keyId: MUX_SIGNING_KEY_ID,
|
||||
keySecret: MUX_SIGNING_KEY_PRIVATE_KEY,
|
||||
params: {
|
||||
playback_restriction_id: MUX_PLAYBACK_RESTRICTION_ID
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
tokens.storyboardToken = JWT.signPlaybackId(ctx.query.id, {
|
||||
keyId: MUX_SIGNING_KEY_ID,
|
||||
keySecret: MUX_SIGNING_KEY_PRIVATE_KEY,
|
||||
type: 'storyboard'
|
||||
})
|
||||
|
||||
|
||||
ctx.body = tokens
|
||||
}
|
||||
}))
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"kind": "collectionType",
|
||||
"collectionName": "tag_vod_relations",
|
||||
"info": {
|
||||
"singularName": "tag-vod-relation",
|
||||
"pluralName": "tag-vod-relations",
|
||||
"displayName": "Tag Vod Relation",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": false
|
||||
},
|
||||
"pluginOptions": {},
|
||||
"attributes": {
|
||||
"votes": {
|
||||
"type": "integer"
|
||||
},
|
||||
"creator": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "plugin::users-permissions.user"
|
||||
},
|
||||
"tag": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::tag.tag"
|
||||
},
|
||||
"vod": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::vod.vod"
|
||||
},
|
||||
"creatorId": {
|
||||
"type": "integer",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* tag-vod-relation controller
|
||||
*/
|
||||
|
||||
const { createCoreController } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreController('api::tag-vod-relation.tag-vod-relation', ({ strapi }) => ({
|
||||
async relate(ctx) {
|
||||
|
||||
const userId = ctx?.state?.user?.id;
|
||||
if (!userId) return ctx.badRequest("There was no user id in the request!");
|
||||
if (!ctx.request.body.data) return ctx.badRequest('data was missing from body');
|
||||
if (!ctx.request.body.data.tag) return ctx.badRequest('tag was missing from data');
|
||||
if (!ctx.request.body.data.vod) return ctx.badRequest('vod was missing from data');
|
||||
|
||||
const { tag: tagId, vod: vodId } = ctx.request.body.data;
|
||||
|
||||
const tagVodRelation = await strapi.entityService.create('api::tag-vod-relation.tag-vod-relation', {
|
||||
data: {
|
||||
vod: vodId,
|
||||
tag: tagId,
|
||||
creator: userId,
|
||||
creatorId: userId,
|
||||
publishedAt: new Date(),
|
||||
votes: 2
|
||||
}
|
||||
})
|
||||
|
||||
return tagVodRelation
|
||||
},
|
||||
async vote(ctx) {
|
||||
// @todo
|
||||
},
|
||||
|
||||
// // greets https://docs.strapi.io/dev-docs/backend-customization/controllers#extending-core-controllers
|
||||
// // greets https://docs.strapi.io/dev-docs/backend-customization/controllers#adding-a-new-controller
|
||||
// // Method 2: Wrapping a core action (leaves core logic in place)
|
||||
// async find(ctx) {
|
||||
// // // some custom logic here
|
||||
// // ctx.query = { ...ctx.query, local: 'en' }
|
||||
|
||||
// const userId = ctx?.state?.user?.id;
|
||||
// if (!userId) return ctx.badRequest("There was no user id in the request!");
|
||||
|
||||
|
||||
|
||||
// // Calling the default core action
|
||||
// const { data, meta } = await super.find(ctx);
|
||||
|
||||
// // add isCreator if the tvr was created by this user
|
||||
// let dataWithCreator = data.map((d) => {
|
||||
// if (d.data.attributes.)
|
||||
// })
|
||||
|
||||
// // // some more custom logic
|
||||
// // meta.date = Date.now()
|
||||
|
||||
// return { data, meta };
|
||||
// },
|
||||
|
||||
// greets https://docs.strapi.io/dev-docs/backend-customization/controllers#extending-core-controllers
|
||||
// greets https://docs.strapi.io/dev-docs/backend-customization/controllers#adding-a-new-controller
|
||||
// Method 2: Wrapping a core action (leaves core logic in place)
|
||||
async create(ctx) {
|
||||
// only allow unique tag, vod combos
|
||||
|
||||
const { query } = ctx.request;
|
||||
|
||||
const userId = ctx?.state?.user?.id;
|
||||
if (!userId) return ctx.badRequest("There was no user id in the request!");
|
||||
if (!ctx.request.body.data) return ctx.badRequest('data was missing from body');
|
||||
if (!ctx.request.body.data.tag) return ctx.badRequest('tag was missing from data');
|
||||
if (!ctx.request.body.data.vod) return ctx.badRequest('vod was missing from data');
|
||||
|
||||
const { tag: tagId, vod: vodId } = ctx.request.body.data;
|
||||
|
||||
const combo = await strapi.entityService.findMany('api::tag-vod-relation.tag-vod-relation', {
|
||||
populate: {
|
||||
tag: true,
|
||||
vod: true
|
||||
},
|
||||
filters: {
|
||||
$and: [{
|
||||
tag: {
|
||||
id: {
|
||||
$eq: ctx.request.body.data.tag
|
||||
}
|
||||
}
|
||||
}, {
|
||||
vod: {
|
||||
id: {
|
||||
$eq: ctx.request.body.data.vod
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
if (combo.length > 0) {
|
||||
return ctx.badRequest('this vod already has that tag')
|
||||
}
|
||||
|
||||
// @todo add votes and creator
|
||||
ctx.request.body.data.creator = userId
|
||||
ctx.request.body.data.votes = 2
|
||||
|
||||
const parseBody = (ctx) => {
|
||||
if (ctx.is('multipart')) {
|
||||
return parseMultipartData(ctx);
|
||||
}
|
||||
|
||||
const { data } = ctx.request.body || {};
|
||||
|
||||
return { data };
|
||||
};
|
||||
|
||||
|
||||
const sanitizedInputData = {
|
||||
vod: vodId,
|
||||
tag: tagId,
|
||||
publishedAt: new Date(),
|
||||
creator: userId,
|
||||
creatorId: userId,
|
||||
votes: 2
|
||||
}
|
||||
|
||||
// const tagVodRelation = await strapi.entityService.create('api::tag-vod-relation.tag-vod-relation', {
|
||||
// ,
|
||||
// populate: {
|
||||
// tag: true,
|
||||
// vod: true
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
|
||||
const entity = await strapi
|
||||
.service('api::tag-vod-relation.tag-vod-relation')
|
||||
.create({
|
||||
...query,
|
||||
data: sanitizedInputData,
|
||||
populate: { vod: true, tag: true }
|
||||
});
|
||||
|
||||
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
||||
|
||||
console.log(sanitizedEntity)
|
||||
|
||||
return this.transformResponse({ ...sanitizedEntity });
|
||||
},
|
||||
|
||||
|
||||
async tagVod (ctx) {
|
||||
|
||||
let tagEntry
|
||||
let tagVodRelationEntry
|
||||
|
||||
// create tag if needed
|
||||
const { query } = ctx.request;
|
||||
const userId = ctx?.state?.user?.id;
|
||||
if (!userId) return ctx.badRequest("There was no user id in the request!");
|
||||
if (!ctx.request.body.data) return ctx.badRequest('data was missing from body');
|
||||
if (!ctx.request.body.data.tagName) return ctx.badRequest('tagName was missing from data');
|
||||
if (!ctx.request.body.data.vodId) return ctx.badRequest('vodId was missing from data');
|
||||
const { tagName, vodId } = ctx.request.body.data;
|
||||
|
||||
// does the named tag already exist?
|
||||
tagEntry = await strapi.db.query('api::tag.tag')
|
||||
.findOne({ where: { name: tagName }})
|
||||
|
||||
// create the named tag if it doesn't exist
|
||||
if (!tagEntry) {
|
||||
tagEntry = await strapi.entityService.create('api::tag.tag', {
|
||||
data: {
|
||||
name: tagName,
|
||||
creator: userId,
|
||||
publishedAt: new Date(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// create tag-vod-relation
|
||||
tagVodRelationEntry = await strapi.entityService.create('api::tag-vod-relation.tag-vod-relation', {
|
||||
data: {
|
||||
tag: tagEntry.id,
|
||||
vod: vodId,
|
||||
creator: userId,
|
||||
creatorId: userId,
|
||||
},
|
||||
populate: {
|
||||
tag: true,
|
||||
vod: true
|
||||
}
|
||||
})
|
||||
|
||||
const sanitizedEntity = await this.sanitizeOutput(tagVodRelationEntry, ctx);
|
||||
return this.transformResponse({ ...sanitizedEntity });
|
||||
|
||||
},
|
||||
|
||||
async deleteMine (ctx) {
|
||||
// // some custom logic here
|
||||
// ctx.query = { ...ctx.query, local: 'en' }
|
||||
|
||||
|
||||
|
||||
const userId = ctx?.state?.user?.id;
|
||||
if (!userId) return ctx.badRequest("There was no user id in the request!");
|
||||
if (!ctx.request.params.id) return ctx.badRequest('id was missing from params');
|
||||
const { id } = ctx.request.params;
|
||||
|
||||
// constraints
|
||||
// only able to delete tagVodRelation if
|
||||
// * creator
|
||||
// * publishedAt isBefore(now-24h)
|
||||
|
||||
|
||||
// get the tvr the user wants to delete
|
||||
const tvrToDelete = await strapi.entityService.findOne('api::tag-vod-relation.tag-vod-relation', id, {
|
||||
populate: {
|
||||
tag: true,
|
||||
vod: true,
|
||||
creator: true,
|
||||
}
|
||||
})
|
||||
|
||||
if (!tvrToDelete) return ctx.badRequest('Tag to be deleted does not exist.');
|
||||
|
||||
if (tvrToDelete.creator.id !== userId)
|
||||
ctx.forbidden('only the creator of the tag can delete it');
|
||||
|
||||
if ((new Date(tvrToDelete.createdAt).valueOf()+86400000) < new Date().valueOf())
|
||||
ctx.forbidden('cannot delete tags older than 24 hours')
|
||||
|
||||
// Calling the default core action
|
||||
const { data, meta } = await super.delete(ctx);
|
||||
|
||||
// delete the related tag if it has no other vod
|
||||
// @todo?? or maybe this is handled by lifecycle hook?
|
||||
|
||||
// // some more custom logic
|
||||
// meta.date = Date.now()
|
||||
|
||||
return { data, meta };
|
||||
}
|
||||
|
||||
|
||||
}));
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* tag-vod-relation router
|
||||
*/
|
||||
|
||||
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||
|
||||
const defaultRouter = createCoreRouter('api::tag-vod-relation.tag-vod-relation');
|
||||
|
||||
// greets https://forum.strapi.io/t/how-to-add-custom-routes-to-core-routes-in-strapi-4/14070/7
|
||||
const customRouter = (innerRouter, extraRoutes = []) => {
|
||||
let routes;
|
||||
return {
|
||||
get prefix() {
|
||||
return innerRouter.prefix;
|
||||
},
|
||||
get routes() {
|
||||
if (!routes) routes = extraRoutes.concat(innerRouter.routes)
|
||||
return routes;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const myExtraRoutes = [
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tag-vod-relations/relate",
|
||||
handler: "api::tag-vod-relation.tag-vod-relation.relate"
|
||||
},
|
||||
// {
|
||||
// method: 'GET',
|
||||
// path: '/tag-vod-relations',
|
||||
// handler: 'api::tag-vod-relation.tag-vod-relation.find'
|
||||
// },
|
||||
{
|
||||
method: "PUT",
|
||||
path: "/tag-vod-relations/vote",
|
||||
handler: "api::tag-vod-relation.tag-vod-relation.vote"
|
||||
}, {
|
||||
method: 'POST',
|
||||
path: '/tag-vod-relations/tag',
|
||||
handler: 'api::tag-vod-relation.tag-vod-relation.tagVod'
|
||||
}, {
|
||||
method: 'DELETE',
|
||||
path: '/tag-vod-relations/deleteMine/:id',
|
||||
handler: 'api::tag-vod-relation.tag-vod-relation.deleteMine'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = customRouter(defaultRouter, myExtraRoutes);
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* tag-vod-relation service
|
||||
*/
|
||||
|
||||
const { createCoreService } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreService('api::tag-vod-relation.tag-vod-relation');
|
|
@ -28,6 +28,11 @@
|
|||
"relation": "manyToMany",
|
||||
"target": "api::vod.vod",
|
||||
"inversedBy": "tags"
|
||||
},
|
||||
"creator": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "plugin::users-permissions.user"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,67 @@
|
|||
|
||||
const { createCoreController } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreController('api::tag.tag');
|
||||
module.exports = createCoreController('api::tag.tag', ({ strapi }) => ({
|
||||
async createTagRelation(ctx) {
|
||||
|
||||
// we have this controller which associates a tag with a vod
|
||||
// this exists so users can indirectly update vod records which they dont have permissions to update
|
||||
// first we need to get the user's request.
|
||||
// they are telling us a vod ID and a tag ID
|
||||
// our job is to get a reference to the vod, and add the tag relation.
|
||||
|
||||
if (!ctx.request.body.data) return ctx.badRequest('data was missing from body');
|
||||
if (!ctx.request.body.data.tag) return ctx.badRequest('tag was missing from data');
|
||||
if (!ctx.request.body.data.vod) return ctx.badRequest('vod was missing from data');
|
||||
|
||||
const { tag, vod: vodId } = ctx.request.body.data;
|
||||
|
||||
// const vod = await strapi.entityService.findOne('api::vod.vod', vodId, {
|
||||
// populate: { tags: true }
|
||||
// })
|
||||
|
||||
|
||||
// console.log(vod)
|
||||
|
||||
// const existingTags = vod.attributes.tags
|
||||
// const updatedTags = vod.attributes.tags.concat([tag])
|
||||
|
||||
// await strapi.entityService.update('api::vod.vod', vodId, {
|
||||
// data: {
|
||||
// tags: updatedTags
|
||||
// }
|
||||
// })
|
||||
|
||||
await strapi.entityService.update('api::vod.vod', vodId, {
|
||||
data: {
|
||||
tags: {
|
||||
connect: [tag]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return 'OK'
|
||||
|
||||
|
||||
// const userId = ctx.state.user.id;
|
||||
// console.log(`createTagRelation controller hit by userID ${userId}`)
|
||||
// console.log(ctx.request.body.data)
|
||||
|
||||
// if (!ctx.request.body.data) return ctx.badRequest("data was missing in request body");
|
||||
// if (!ctx.request.body.data.date) return ctx.badRequest("date was missing");
|
||||
// if (!ctx.request.body.data.videoSrcB2) return ctx.badRequest("videoSrcB2 was missing");
|
||||
// if (!ctx.request.body.data.videoSrcB2.key) return ctx.badRequest("videoSrcB2.key was missing");
|
||||
// if (!ctx.request.body.data.videoSrcB2.uploadId) return ctx.badRequest("videoSrcB2.uploadId was missing");
|
||||
|
||||
|
||||
// console.log(ctx)
|
||||
|
||||
// const publicPatrons = await strapi.entityService.findMany('plugin::users-permissions.user', {
|
||||
// fields: ['username'],
|
||||
// filters: { isNamePublic: true }
|
||||
// })
|
||||
// console.log(publicPatrons)
|
||||
// return publicPatrons
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -5,5 +5,28 @@
|
|||
*/
|
||||
|
||||
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||
const defaultRouter = createCoreRouter('api::tag.tag');
|
||||
|
||||
module.exports = createCoreRouter('api::tag.tag');
|
||||
// greets https://forum.strapi.io/t/how-to-add-custom-routes-to-core-routes-in-strapi-4/14070/7
|
||||
const customRouter = (innerRouter, extraRoutes = []) => {
|
||||
let routes;
|
||||
return {
|
||||
get prefix() {
|
||||
return innerRouter.prefix;
|
||||
},
|
||||
get routes() {
|
||||
if (!routes) routes = extraRoutes.concat(innerRouter.routes)
|
||||
return routes;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const myExtraRoutes = [
|
||||
{
|
||||
method: "POST",
|
||||
path: "/tag/tagRelation",
|
||||
handler: "api::tag.tag.createTagRelation"
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = customRouter(defaultRouter, myExtraRoutes)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"kind": "collectionType",
|
||||
"collectionName": "timestamps",
|
||||
"info": {
|
||||
"singularName": "timestamp",
|
||||
"pluralName": "timestamps",
|
||||
"displayName": "Timestamp",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": false,
|
||||
"populateCreatorFields": true
|
||||
},
|
||||
"pluginOptions": {},
|
||||
"attributes": {
|
||||
"time": {
|
||||
"type": "integer"
|
||||
},
|
||||
"tag": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::tag.tag"
|
||||
},
|
||||
"vod": {
|
||||
"type": "relation",
|
||||
"relation": "manyToOne",
|
||||
"target": "api::vod.vod",
|
||||
"inversedBy": "timestamps"
|
||||
},
|
||||
"creatorId": {
|
||||
"type": "integer",
|
||||
"required": true
|
||||
},
|
||||
"upvoters": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "plugin::users-permissions.user"
|
||||
},
|
||||
"downvoters": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "plugin::users-permissions.user"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* timestamp controller
|
||||
*/
|
||||
|
||||
const { createCoreController } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreController('api::timestamp.timestamp', ({ strapi }) => ({
|
||||
|
||||
|
||||
async find(ctx) {
|
||||
const { data, meta } = await super.find(ctx);
|
||||
|
||||
// Iterate over each timestamp in the data array
|
||||
const timestampsWithVotes = await Promise.all(data.map(async (timestamp) => {
|
||||
// Retrieve the upvoters count for the current timestamp
|
||||
// Retrieve the downvoters count for the current timestamp
|
||||
const entry = await strapi.db
|
||||
.query('api::timestamp.timestamp')
|
||||
.findOne({
|
||||
populate: ['upvoters', 'downvoters'],
|
||||
where: {
|
||||
id: timestamp.id
|
||||
}
|
||||
});
|
||||
|
||||
const upvotesCount = entry.upvoters.length
|
||||
const downvotesCount = entry.downvoters.length
|
||||
|
||||
// Create new properties "upvotes" and "downvotes" on the timestamp object
|
||||
timestamp.attributes.upvotes = upvotesCount;
|
||||
timestamp.attributes.downvotes = downvotesCount;
|
||||
|
||||
return timestamp;
|
||||
}));
|
||||
|
||||
console.log(timestampsWithVotes)
|
||||
|
||||
return { data: timestampsWithVotes, meta };
|
||||
},
|
||||
|
||||
|
||||
// greets https://docs.strapi.io/dev-docs/backend-customization/controllers#extending-core-controllers
|
||||
// greets https://docs.strapi.io/dev-docs/backend-customization/controllers#adding-a-new-controller
|
||||
// Method 2: Wrapping a core action (leaves core logic in place)
|
||||
async create(ctx) {
|
||||
// add creatorId to the record
|
||||
const userId = ctx?.state?.user?.id;
|
||||
if (!userId) return ctx.badRequest("There was no user id in the request!");
|
||||
if (!ctx.request.body.data) return ctx.badRequest('data was missing from body');
|
||||
if (!ctx.request.body.data.tag) return ctx.badRequest('tag was missing from data');
|
||||
const { time, tag } = ctx.request.body.data;
|
||||
|
||||
ctx.request.body.data.creatorId = userId
|
||||
console.log(ctx.request.body.data)
|
||||
|
||||
|
||||
|
||||
// does the timestamp already exist with same combination of time+tag?
|
||||
const duplicate = await strapi.db.query('api::timestamp.timestamp')
|
||||
.findOne({ where: { time, tag }})
|
||||
|
||||
if (!!duplicate) return ctx.badRequest('A duplicate timestamp already exists!');
|
||||
|
||||
// Calling the default core action
|
||||
const res = await super.create(ctx);
|
||||
|
||||
return res
|
||||
},
|
||||
|
||||
|
||||
async vote(ctx) {
|
||||
const userId = ctx?.state?.user?.id;
|
||||
const { direction } = ctx.request.body.data;
|
||||
if (!ctx.request.params.id) return ctx.badRequest('id was missing from params');
|
||||
const { id } = ctx.request.params;
|
||||
// get the ts to be voted on
|
||||
const ts = await strapi.entityService.findOne('api::timestamp.timestamp', id)
|
||||
if (!ts) return ctx.badRequest('timestamp does not exist');
|
||||
const res = await strapi.entityService.update('api::timestamp.timestamp', id, {
|
||||
data: {
|
||||
upvoters: direction === 1 ? { connect: [userId] } : { disconnect: [userId] },
|
||||
downvoters: direction === 1 ? { disconnect: [userId] } : { connect: [userId] }
|
||||
}
|
||||
});
|
||||
return res;
|
||||
},
|
||||
|
||||
|
||||
|
||||
async delete(ctx) {
|
||||
const userId = ctx?.state?.user?.id;
|
||||
const { id } = ctx.request.params;
|
||||
|
||||
// get the ts to be deleted
|
||||
const ts = await strapi.entityService.findOne('api::timestamp.timestamp', id)
|
||||
if (!ts) return ctx.badRequest('Timestamp does not exist')
|
||||
|
||||
|
||||
// Refuse to delete if not the tag creator
|
||||
if (ts.creatorId !== userId) return ctx.forbidden('Only the timestamp creator can delete the timestamp.')
|
||||
|
||||
|
||||
const res = await super.delete(ctx)
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
}))
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* timestamp router
|
||||
*/
|
||||
|
||||
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||
|
||||
|
||||
const defaultRouter = createCoreRouter('api::timestamp.timestamp');
|
||||
|
||||
// greets https://forum.strapi.io/t/how-to-add-custom-routes-to-core-routes-in-strapi-4/14070/7
|
||||
const customRouter = (innerRouter, extraRoutes = []) => {
|
||||
let routes;
|
||||
return {
|
||||
get prefix() {
|
||||
return innerRouter.prefix;
|
||||
},
|
||||
get routes() {
|
||||
if (!routes) routes = extraRoutes.concat(innerRouter.routes)
|
||||
return routes;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
const myExtraRoutes = [
|
||||
{
|
||||
method: "PUT",
|
||||
path: "/timestamps/:id/vote",
|
||||
handler: "api::timestamp.timestamp.vote"
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/timestamps/:id',
|
||||
handler: 'api::timestamp.timestamp.delete'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = customRouter(defaultRouter, myExtraRoutes);
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* timestamp service
|
||||
*/
|
||||
|
||||
const { createCoreService } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreService('api::timestamp.timestamp');
|
|
@ -38,6 +38,11 @@
|
|||
"allowedTypes": [
|
||||
"images"
|
||||
]
|
||||
},
|
||||
"linkTag": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::tag.tag"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,12 @@
|
|||
"relation": "manyToMany",
|
||||
"target": "api::tag.tag",
|
||||
"mappedBy": "vods"
|
||||
},
|
||||
"timestamps": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "api::timestamp.timestamp",
|
||||
"mappedBy": "vod"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,17 @@
|
|||
"avatar": {
|
||||
"type": "string",
|
||||
"default": "http://placekitten.com/32/32"
|
||||
},
|
||||
"isLinkPublic": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"vanityLink": {
|
||||
"type": "string",
|
||||
"maxLength": 256
|
||||
},
|
||||
"patreonBenefits": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ module.exports = ({ strapi }) => {
|
|||
console.log(` >> HERE is the user's patreon profile`)
|
||||
console.log(profile)
|
||||
const isPatron = profile.benefits.includes(patreonModel.benefitId)
|
||||
const patreonBenefits = profile.benefits.join(',')
|
||||
|
||||
console.log(`isPatron:${isPatron}`)
|
||||
|
||||
|
@ -126,7 +127,8 @@ module.exports = ({ strapi }) => {
|
|||
where: { email },
|
||||
data: {
|
||||
...user,
|
||||
role: selectedRole
|
||||
role: selectedRole,
|
||||
patreonBenefits
|
||||
}
|
||||
})
|
||||
return updatedUser;
|
||||
|
|
22
yarn.lock
22
yarn.lock
|
@ -6603,6 +6603,11 @@ function-bind@^1.1.1:
|
|||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
fuzzysort@2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-2.0.4.tgz#a21d1ce8947eaf2797dc3b7c28c36db9d1165f84"
|
||||
integrity sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
|
@ -10703,6 +10708,14 @@ std-env@^3.0.1:
|
|||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.2.tgz#af27343b001616015534292178327b202b9ee955"
|
||||
integrity sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==
|
||||
|
||||
strapi-plugin-fuzzy-search@^1.11.0-beta.1:
|
||||
version "1.11.0-beta.1"
|
||||
resolved "https://registry.yarnpkg.com/strapi-plugin-fuzzy-search/-/strapi-plugin-fuzzy-search-1.11.0-beta.1.tgz#fadd58e6cb5278bc793179f649cc19b77ed4103c"
|
||||
integrity sha512-hMOfgONiN5b7drMI8R3IhVoDXUpzGcgRM/zkSPLWymJDiJEPVaVyIexSoJhll+syZUmHZB3WXq/oK/sWiizekQ==
|
||||
dependencies:
|
||||
fuzzysort "2.0.4"
|
||||
transliteration "2.3.5"
|
||||
|
||||
stream-chain@2.2.5, stream-chain@^2.2.5:
|
||||
version "2.2.5"
|
||||
resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09"
|
||||
|
@ -11049,6 +11062,13 @@ tr46@~0.0.3:
|
|||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
transliteration@2.3.5:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/transliteration/-/transliteration-2.3.5.tgz#8f92309575f69e4a8a525dab4ff705ebcf961c45"
|
||||
integrity sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==
|
||||
dependencies:
|
||||
yargs "^17.5.1"
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
|
@ -11676,7 +11696,7 @@ yargs-parser@^21.1.1:
|
|||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
||||
yargs@^17.7.1:
|
||||
yargs@^17.5.1, yargs@^17.7.1:
|
||||
version "17.7.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
||||
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
||||
|
|
Loading…
Reference in New Issue