Strapi media library image url broken by Digital Ocean - file-upload

I am trying to upload an image to Digital Ocean Storage. it's upload in DO but after callback strapi generate wrong URL
for Example: https://https//jobsflow/d0e989a489bdc380c55e5846076d07f8.png?updated_at=2022-06-08T17:00:32.934Z thats mean https://https//.
//jobsflow is my location of storage.
here is my config/plugins.js code
module.exports = {
upload: {
config: {
provider: "strapi-provider-upload-dos",
providerOptions: {
key: process.env.DO_SPACE_ACCESS_KEY,
secret: process.env.DO_SPACE_SECRET_KEY,
endpoint: process.env.DO_SPACE_ENDPOINT,
space: process.env.DO_SPACE_BUCKET,
directory: process.env.DO_SPACE_DIRECTORY,
cdn: process.env.DO_SPACE_CDN,
},
},
},
};
//here is my config/middleware.js
module.exports = [
"strapi::errors",
{
name: "strapi::security",
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
"connect-src": ["'self'", "https:"],
"img-src": [
"'self'",
"data:",
"blob:",
"*.digitaloceanspaces.com"
],
"media-src": ["'self'", "data:", "blob:"],
upgradeInsecureRequests: null,
},
},
},
},
"strapi::cors",
"strapi::poweredBy",
"strapi::logger",
"strapi::query",
"strapi::body",
"strapi::favicon",
"strapi::public",
];
please help me..! if you have any idea

Are you using a custom upload provider for this?
Why not use the official #strapi/provider-upload-aws-s3 plugin?
// path config/plugins.js
...
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
accessKeyId: env('DO_ACCESS_KEY_ID'),
secretAccessKey: env('DO_ACCESS_SECRET'),
region: env('DO_REGION'),
endpoint: env('DO_ENDPOINT'),
params: {
Bucket: env('DO_BUCKET'),
}
},
},
},
Another nice trick to change the URL of an image and point it to your CDN is adding this:
// src/index.js
async bootstrap({strapi}){
strapi.db.lifecycles.subscribe({
models: ['plugin::upload.file'],
// use cdn url instead of space origin
async beforeCreate(data) {
data.params.data.url = data.params.data.url.replace(__ORIGINAL_URL__, __CDN_URL__)
// you can even do more here like setting policies for the object you're uploading
},
});
}

Related

Vue.js app getting 404 error when requesting data from the backend using proxy

all,
I am writing a front-end of my app, using Vue. I need to request some information from the back-end, launched on the same server.
I added proxy like this:
module.exports = {
devServer: {
port: 2611,
proxy: {
'/api/markets/light': {
target: 'http://localhost:2610/markets/light',
ws: true,
logLevel: 'debug',
pathRewrite: {
'^/api/markets/light': '/markets/light',
},
},
},
},
}
(I've tried different combinations of the parameters, like excluding pathRewrite, '^/api/...' instead of '/api/...', etc.)
This is how data is requested from the code:
created() {
axios.get(this.$store.getters.marketsLightLink)
.then(response => this.markets = response.data)
.catch(error => console.log(error));
}
marketsLightLink simply concatenates strings:
const store = new Vuex.Store({
state: {
marketDataHost: '/api',
markets: {
markets: '/markets',
marketLight: '/markets/light',
},
},
getters: {
marketsLightLink(state) {
return state.marketDataHost + state.markets.marketLight;
},
},
});
So when i open the page, where i should see the results of the request, i just see 404 error in the browser's console and no data downloaded. At the same time I see, that the link is proxied correctly:
[HPM] Rewriting path from "/api/markets/light" to "/markets/light"
[HPM] GET /api/markets/light ~> http://localhost:2610/markets/light
And when i press the resulting link, the requested information is shown in the browser.
Can anyone help me, what am I doing wrong please?

Keystone.js 6 access denied adminMeta

i want to seed data onConnect, but i have access denied, using this query :
{
keystone: keystone {
adminMeta {
lists {
key
description
label
singular
plural
path
fields {
path
}
}
}
}
i have this error even iam using sudo, context.sudo().graphql.raw :
[
Error: Access denied
at /Users/sidalitemkit/work/web/yet/wirxe/wirxe-app/node_modules/#keystone-next/admin-ui/system/dist/admin-ui.cjs.dev.js:552:19
at processTicksAndRejections (node:internal/process/task_queues:94:5)
at async Promise.all (index 0)
at async Promise.all (index 0) {
locations: [ [Object] ],
path: [ 'keystone', 'adminMeta' ]
}
]
here my config :
export default auth.withAuth(
config({
db: {
adapter: 'prisma_postgresql',
url:
'postgres://admin:aj093bf7l6jdx5hm#wirxe-app-database-do-user-9126376-0.b.db.ondigitalocean.com:25061/wirxepool?schema=public&pgbouncer=true&sslmode=require',
onConnect: initialiseData,
},
ui: {
isAccessAllowed: (context) => !!context.session?.data,
},
lists,
session: withItemData(
statelessSessions({
maxAge: sessionMaxAge,
secret: sessionSecret,
}),
{ User: 'email' },
),
}),
);
i figured out that when i do :
isAccessAllowed: (context) => true
it's working
any advice here
context.sudo() disabled access control. there could be some issue with your query. isAccessAllowed: (context) => true is related to admin-ui and not to the backend implementation of graphql. This could be a bug please open a bug in the repo. They whould be able to fix it quickly.
I do not see sample initialiseData to try myself. Also the graphql is designed as such if you try to access some non existing item then it may give you access denied error even though there is not access control (all access set to true).
There is also another api which is easier in creating the initial items. You should use new list api, available as context.sudo().lists.<ListName>.createOne or createMany like this
const user = await context.sudo().lists.User.createOne({
data: {
name: 'Alice',
posts: { create: [{ title: 'My first post' }] },
},
query: 'id name posts { id title }',
});
or
const users = await context.lists.User.createOne({
data: [
{
data: {
name: 'Alice',
posts: [{ create: { title: 'Alices first post' } }],
},
},
{
data: {
name: 'Bob',
posts: [{ create: { title: 'Bobs first post' } }],
},
},
],
query: 'id name posts { id title }',
});
for more details see List Items API and Database Items API in their preview documentation.
You can find a working example in keystonejs repository (blog)
You have to await and pass context to the initialiseData() method. The onConnect hook already provides this context for you
also, you can look for an argument like '--seed-data' so it's only run once
and run the code as:
keystone --seed-data
export default auth.withAuth(
config({
db: {
adapter: 'prisma_postgresql',
url:
'postgres://admin:aj093bf7l6jdx5hm#wirxe-app-database-do-user-9126376-0.b.db.ondigitalocean.com:25061/wirxepool?schema=public&pgbouncer=true&sslmode=require',
async onConnect(context) {
if (process.argv.includes('--seed-data')) {
await initialiseData(context);
}
},
},
ui: {
isAccessAllowed: (context) => !!context.session?.data,
},
lists,
session: withItemData(
statelessSessions({
maxAge: sessionMaxAge,
secret: sessionSecret,
}),
{ User: 'email' },
),
}),
);

Serverless framework lambda function access denied to S3

Anyone have any ideas why I'm getting "Access Denied" when trying to put object into S3 inside a lambda function? I have the serverless AWS user with AdministorAccess and allow access to s3 resource inside serverless.yml:
iamRoleStatements:
- Effect: Allow
Action:
- s3:PutObject
Resource: "arn:aws:s3:::*"
Edit - here are the files
serverless.yml
service: testtest
app: testtest
org: workx
provider:
name: aws
runtime: nodejs12.x
iamRoleStatements:
- Effect: Allow
Action:
- s3:PutObject
Resource: "arn:aws:s3:::*/*"
functions:
hello:
handler: handler.hello
events:
- http:
path: users/create
method: get
handler.js
'use strict';
const AWS = require('aws-sdk');
// get reference to S3 client
const S3 = new AWS.S3();
// Uload the content to s3 and allow download
async function uploadToS3(content) {
console.log('going to upload to s3!');
const Bucket = 'mtest-exports';
const key = 'testtest.csv';
try {
const destparams = {
Bucket,
Key: key,
Body: content,
ContentType: "text/csv",
};
console.log('going to put object', destparams);
const putResult = await S3.putObject(destparams).promise();
return putResult;
} catch (error) {
console.log(error);
throw error;
}
}
module.exports.hello = async event => {
const result = await uploadToS3('hello world');
return {
statusCode: 200,
body: JSON.stringify(result),
};
};
I was using TypeScript plugin - #serverless/typescript. I used it to create Lambda function that will resize images that are uploaded to S3 + do some kind of content moderation.
Here is the content of serverless.ts file:
import type { AWS } from '#serverless/typescript';
import resizeImageLambda from '#functions/resizeImageLambda';
const serverlessConfiguration: AWS = {
service: 'myservice-image-resize',
frameworkVersion: '3',
plugins: ['serverless-esbuild'],
provider: {
name: 'aws',
stage: 'dev',
region: 'us-east-1',
profile: 'myProjectProfile', // reference to your local AWS profile created by serverless config command
// architecture: 'arm64', // to support Lambda w/ graviton
iam: {
role: {
statements: [
{
Effect: 'Allow',
Action: [
's3:GetObject',
's3:PutObject',
's3:PutObjectAcl',
's3:ListBucket',
'rekognition:DetectModerationLabels'
],
Resource: [
'arn:aws:s3:::myBucket/*',
'arn:aws:s3:::myBucket',
'arn:aws:s3:::/*',
'*'
]
},
{
Effect: 'Allow',
Action: [
's3:ListBucket',
'rekognition:DetectModerationLabels'
],
Resource: ['arn:aws:s3:::myBucket']
}
]
}
},
// architecture: 'arm64',
runtime: 'nodejs16.x',
environment: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
NODE_OPTIONS: '--enable-source-maps --stack-trace-limit=1000',
SOURCE_BUCKET_NAME:
'${self:custom.myEnvironment.SOURCE_BUCKET_NAME.${self:custom.myStage}}',
DESTINATION_BUCKET_NAME:
'${self:custom.myEnvironment.DESTINATION_BUCKET_NAME.${self:custom.myStage}}'
}
},
// import the function via paths
functions: { resizeImageLambda },
package: { individually: true },
custom: {
esbuild: {
bundle: true,
minify: false,
sourcemap: true,
exclude: ['aws-sdk'],
target: 'node16',
define: { 'require.resolve': undefined },
platform: 'node',
concurrency: 10,
external: ['sharp'],
packagerOptions: {
scripts:
'rm -rf node_modules/sharp && SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux --libc=glibc sharp'
}
},
myEnvironment: {
SOURCE_BUCKET_NAME: {
dev: 'myBucket',
prod: 'myBucket-prod'
},
DESTINATION_BUCKET_NAME: {
dev: 'myBucket',
prod: 'myBucketProd'
}
},
myStage: '${opt:stage, self:provider.stage}'
}
};
module.exports = serverlessConfiguration;
resizeImageLambda.ts
/* eslint-disable no-template-curly-in-string */
// import { Config } from './config';
export const handlerPath = (context: string) =>
`${context.split(process.cwd())[1].substring(1).replace(/\\/g, '/')}`;
export default {
handler: `${handlerPath(__dirname)}/handler.main`,
events: [
{
s3: {
bucket: '${self:custom.myEnvironment.SOURCE_BUCKET_NAME.${self:custom.myStage}}',
event: 's3:ObjectCreated:*',
existing: true,
forceDeploy: true // for existing buckets
}
}
],
timeout: 15 * 60, // 15 min
memorySize: 2048
};
I remember there were few issues when I wanted to connect it to existing buckets (created outside serverless framework) such as IAM policy was not re-created / updated properly (see forceDeploy end existing parameters in function.events[0].s3 properties in resizeLambda.ts file)
Turns out I was an idiot and have the custom config in the wrong place and ruin the serverless.yml file!

CORS blocking client request in Nuxt.js

I am having issues when making a client request.
I have followed the documentation on Nuxt.js and Axios but I still can't seem to get it working. Maybe I am missing something..
My Vue component calling the vuex action:
methods: {
open() {
this.$store.dispatch('events/getEventAlbum');
}
}
The action in vuex:
export const actions = {
async getEventAlbum(store) {
console.log('album action');
const response = await Axios.get(url + '/photos?&sign=' + isSigned + '&photo-host=' + photoHost);
store.commit('storeEventAlbum', response.data.results);
}
};
And my nuxt.js.config
modules: [
'#nuxtjs/axios',
'#nuxtjs/proxy'
],
axios: {
proxy: true
},
proxy: {
'/api/': {
target: 'https://api.example.com/',
pathRewrite: { '^/api/': '' }
}
}
Anybody who can help?
I believe the issue that #andrew1325 is trying to point out is that the API provider needs to have the CORS enabled, not just your server, without changing the headers in your proxy, you're passing the same headers, which at the moment prevent access.
It seems to me that you're only missing changeOrigin
please try the following config:
modules: [
'#nuxtjs/axios',
'#nuxtjs/proxy'
],
axios: {
proxy: true
},
proxy: {
'/api/': { target: 'https://api.example.com/', pathRewrite: {'^/api/': ''}, changeOrigin: true }
}
also make sure that your front-end API url is pointing to your proxied request /api

Run nuxt on https locally – problem with nuxt.config.js

I am trying to run nuxt locally with https to test some geolocation stuff.
(https://nuxtjs.org/, https://nuxtjs.org/api/nuxt)
I was following this tutorial:
https://www.mattshull.com/blog/https-on-localhost
And then I found this:
https://github.com/nuxt/nuxt.js/issues/146
Both links seem to describe pretty nicely how to run nuxt with server.js programmatically.
The thing is that in my nuxt.config.js I seem to have some problems.
I get the following error when runnung yarn dev:
/Users/USER/Documents/github/mynuxtrepo/nuxt.config.js:2
import { module } from 'npmmodule'
> SyntaxError: Unexpected token {
In my nuxt config I import a custom helper to generate localized routes. Not really important what it does but obviously it can't handle the import syntax.
I assume that the node version does not understand.
So how can I get it to run? Do I have to require everything instead of importing?
Or is my assumption wrong and the cause lies somewhere totally different?
Thank you for your help
Cheers.
------
Edit 1:
My nuxt config looks like this:
// eslint-disable-next-line prettier/prettier
import { generateLocalizedRoutes, generateRoutesFromData } from 'vuecid-helpers'
import config from './config'
// TODO: Add your post types
const postTypes = [{ type: 'pages' }, { type: 'posts', paginated: true }]
// TODO: Add your site title
const siteTitle = 'Title'
const shortTitle = 'short title'
const siteDescription = 'Page demonstrated with a wonderful example'
const themeColor = '#ffffff'
// TODO: Replace favicon source file in /static/icon.png (512px x 512px)
// eslint-disable-next-line prettier/prettier
const iconSizes = [32, 57, 60, 72, 76, 144, 120, 144, 152, 167, 180, 192, 512]
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Loading…',
htmlAttrs: {
lang: config.env.DEFAULTLANG,
dir: 'ltr' // define directionality of text globally
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
// TODO: Check this info
{ name: 'author', content: 'Author' },
{ name: 'theme-color', content: themeColor },
// TODO: Add or remove google-site-verification
{
name: 'google-site-verification',
content: '...W1GdU'
}
],
link: []
},
/*
** env: lets you create environment variables that will be shared for the client and server-side.
*/
env: config.env,
/*
** Customize the progress-bar color
** TODO: Set your desired loading bar color
*/
loading: { color: '#0000ff' },
/*
** CSS
*/
css: ['#/assets/css/main.scss'],
/*
** Plugins
*/
plugins: [
{ src: '~/plugins/global.js' },
{ src: '~/plugins/throwNuxtError.js' },
{ src: '~/plugins/axios' },
{ src: '~/plugins/whatinput.js', ssr: false },
{ src: '~/plugins/i18n.js', injectAs: 'i18n' },
{ src: '~/plugins/vuex-router-sync' },
{ src: '~/plugins/vue-bows' },
{ src: '~/plugins/vue-breakpoint-component', ssr: false }
],
/*
** Modules
*/
modules: [
'#nuxtjs/axios',
'#nuxtjs/sitemap',
[
'#nuxtjs/pwa',
{
icon: {
sizes: iconSizes
},
// Override certain meta tags
meta: {
viewport: 'width=device-width, initial-scale=1',
favicon: true // Generates only apple-touch-icon
},
manifest: {
name: siteTitle,
lang: config.env.DEFAULTLANG,
dir: 'ltr',
short_name: shortTitle,
theme_color: themeColor,
start_url: '/',
display: 'standalone',
background_color: '#fff',
description: siteDescription
}
}
]
],
/*
** Workbox config
*/
workbox: {
config: {
debug: false,
cacheId: siteTitle
}
},
/*
** Axios config
*/
axios: {
baseURL: '/'
},
/*
** Generate
*/
generate: {
subFolders: true,
routes: [
...generateRoutesFromData({
langs: config.env.LANGS,
postTypes: postTypes,
dataPath: '../../../../../static/data',
bundle: 'basic',
homeSlug: config.env.HOMESLUG,
errorPrefix: config.env.ERROR_PREFIX
})
]
},
/*
** Build configuration
*/
build: {
extend(config, { isDev, isClient }) {
/*
** Run ESLINT on save
*/
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
},
/*
** Router
*/
router: {
linkActiveClass: 'is-active',
linkExactActiveClass: 'is-active-exact',
middleware: ['i18n'],
extendRoutes(routes) {
// extends basic routes (based on your files/folders in pages directory) with i18n locales (from our config.js)
const newRoutes = generateLocalizedRoutes({
baseRoutes: routes,
defaultLang: config.env.DEFAULTLANG,
langs: config.env.LANGS,
routesAliases: config.routesAliases
})
// Clear array
routes.splice(0, routes.length)
// Push newly created routes
routes.push(...newRoutes)
}
},
/*
** Sitemap Configuration
*/
sitemap: {
path: '/sitemap.xml',
hostname: config.env.FRONTENDURLPRODUCTION,
cacheTime: 1000 * 60 * 15,
generate: true,
routes: [
...generateRoutesFromData({
langs: config.env.LANGS,
postTypes: postTypes,
dataPath: '../../../../../static/data',
bundle: 'basic',
homeSlug: config.env.HOMESLUG,
errorPrefix: config.env.ERROR_PREFIX
})
]
}
}
You can see that the generateLocalizedRoutes and the generateRoutesFromData methods are used to generate localized routes and is also taking post json files to generate routes from data (:slug).
--------- Edit 2:
I got it to run eventually.
I had to require all parts within the nuxt.config.js instead of importing them. I also resolved issues with the certificates. So I thought it was all cool 🚀.
BUT!!! 🚧:
Then I found out that I had my config file used within my post template.
So I thought I would also require the file within my template:
Like const config = require('~/config').
But then I would get this error:
[nuxt] Error while initializing app TypeError: ""exports" is read-only"
After some research, I found that is probably a thing when using common.js require and module.exports together with ES6 import/export within my project. (Probably linked to: https://github.com/FranckFreiburger/vue-pdf/issues/1).
So how could I still use my config.js when running nuxt programmatically (with require) and then also within my app?
I am glad to hear any ideas on this...
Cheers
Well, just to close this:
My problem resulted from running nuxt as a node app, which does not understand ES6 import statements, which appeared in my nuxt config.
So I had to rewrite things to work with commons.js (require).
This works for now.
(I also tried to run babel-node when starting the server.js, but had no success. Does not mean this did not work, I just wasn't keen on trying harder).
Thanks for the comments.
cheers