diff --git a/package.json b/package.json
index 24c859d7..5367c3b8 100644
--- a/package.json
+++ b/package.json
@@ -58,8 +58,6 @@
"@nivo/bar": "^0.87.0",
"@oclif/core": "^4.0.12",
"@oclif/plugin-warn-if-update-available": "^3.1.11",
- "@sentry/nestjs": "^8.30.0",
- "@sentry/profiling-node": "^8.30.0",
"@tanstack/react-query": "^5.48.0",
"axios": "^1.7.7",
"class-transformer": "^0.5.1",
diff --git a/packages/app/src/api/platformServer/weeklyReport.platformApi.ts b/packages/app/src/api/platformServer/weeklyReport.platformApi.ts
index e0d78db6..a2ddbc18 100644
--- a/packages/app/src/api/platformServer/weeklyReport.platformApi.ts
+++ b/packages/app/src/api/platformServer/weeklyReport.platformApi.ts
@@ -19,13 +19,11 @@ export const useGetAiWeeklyReport = (startOfWeek: string, email?: string) => {
export const useGenerateAiWeeklyReport = () => {
const queryClient = useQueryClient()
- let startOfWeek: string
const mutationFn = (body: {
email: string
startOfWeek: string
weeklyReport: CodeClimbers.WeeklyScores
}) => {
- startOfWeek = body.startOfWeek
return platformApiRequest({
url: `${PLATFORM_API_URL}/ai-weekly-report`,
method: 'POST',
@@ -34,9 +32,9 @@ export const useGenerateAiWeeklyReport = () => {
}
return useMutation({
mutationFn,
- onSuccess: () => {
+ onSuccess: (_, variables) => {
queryClient.invalidateQueries({
- queryKey: weeklyReportKeys.aiWeeklyReports(startOfWeek),
+ queryKey: weeklyReportKeys.aiWeeklyReports(variables.startOfWeek),
})
},
})
diff --git a/packages/app/src/components/Home/Source/SourceTimeChart.tsx b/packages/app/src/components/Home/Source/SourceTimeChart.tsx
index ddbfcd74..bede495c 100644
--- a/packages/app/src/components/Home/Source/SourceTimeChart.tsx
+++ b/packages/app/src/components/Home/Source/SourceTimeChart.tsx
@@ -41,7 +41,7 @@ export const SourceTimeChart = ({
/>
-
+
{time}
diff --git a/packages/app/src/components/common/CodeSnippit/CodeSnippit.tsx b/packages/app/src/components/common/CodeSnippit/CodeSnippit.tsx
index abce81ac..3288b8d2 100644
--- a/packages/app/src/components/common/CodeSnippit/CodeSnippit.tsx
+++ b/packages/app/src/components/common/CodeSnippit/CodeSnippit.tsx
@@ -28,22 +28,40 @@ export const CodeSnippit = ({ code, onCopy }: CodeSnippitProps) => {
}
}, [])
- const copyToClipboard = () => {
+ const copyToClipboard = async () => {
if (errored || copied) return
if (onCopy) onCopy()
+ if (navigator.clipboard && window.isSecureContext) {
+ await navigator.clipboard.writeText(code)
+ setCopied(true)
+ handleTimer(() => {
+ setCopied(false)
+ })
+ } else {
+ const textArea = document.createElement('textarea')
+ textArea.value = code
- if (navigator.clipboard) {
- navigator.clipboard.writeText(code)?.then(() => {
+ textArea.style.position = 'absolute'
+ textArea.style.left = '-999999px'
+
+ document.body.prepend(textArea)
+ textArea.select()
+
+ try {
+ document.execCommand('copy')
setCopied(true)
handleTimer(() => {
setCopied(false)
})
- })
- } else {
- setErrored(true)
- handleTimer(() => {
- setErrored(false)
- })
+ } catch (error) {
+ setErrored(true)
+ handleTimer(() => {
+ setErrored(false)
+ })
+ console.error(error)
+ } finally {
+ textArea.remove()
+ }
}
}
diff --git a/packages/app/src/services/feature.service.ts b/packages/app/src/services/feature.service.ts
index 768b693a..03e6b102 100644
--- a/packages/app/src/services/feature.service.ts
+++ b/packages/app/src/services/feature.service.ts
@@ -8,25 +8,20 @@ export const isFeatureEnabled = (
state: FeatureState,
): boolean => {
const enabledFeatures = JSON.parse(
- localStorage.getItem('enabled-features') || '{}',
+ localStorage.getItem(`enabled-features-${feature}`) || '',
)
- return enabledFeatures[feature] === state
+ return enabledFeatures === state
}
export const setFeatureEnabled = (feature: FeatureKey, state: FeatureState) => {
- const enabledFeatures = JSON.parse(
- localStorage.getItem('enabled-features') || '{}',
- )
- const newEnabledFeatures = { ...enabledFeatures, [feature]: state }
- localStorage.setItem('enabled-features', JSON.stringify(newEnabledFeatures))
- posthog.capture('$set', {
- $set: { 'enabled-features': newEnabledFeatures },
- })
-}
-
-export const getFeaturePreferences = (): Record => {
- const enabledFeatures = JSON.parse(
- localStorage.getItem('enabled-features') || '{}',
- )
- return enabledFeatures
+ let stringState = ''
+ try {
+ stringState = JSON.stringify(state)
+ localStorage.setItem(`enabled-features-${feature}`, stringState)
+ posthog.capture('$set', {
+ $set: { [`enabled-features-${feature}`]: state },
+ })
+ } catch (error) {
+ console.error(`Error setting feature enabled-features-${feature}`, error)
+ }
}
diff --git a/packages/server/commands/log/error.ts b/packages/server/commands/log/error.ts
new file mode 100644
index 00000000..03fec6f9
--- /dev/null
+++ b/packages/server/commands/log/error.ts
@@ -0,0 +1,26 @@
+// oclif command to get the latest 50 lines of error logs from the log file
+import { Command, Flags } from '@oclif/core'
+import path from 'node:path'
+import fs from 'node:fs'
+import { CODE_CLIMBER_META_DIR, ERROR_LOG_NAME } from '../../utils/node.util'
+
+export default class Log extends Command {
+ static description = 'Get the latest 50 lines of error logs'
+
+ static flags = {
+ lines: Flags.string({
+ char: 'l',
+ description: 'Number of lines to get',
+ required: false,
+ }),
+ }
+
+ async run() {
+ const { flags } = await this.parse(Log)
+ const lines = flags.lines || 50
+ this.log(`Getting latest ${lines} lines of error logs...`)
+ const errorLogPath = path.join(CODE_CLIMBER_META_DIR, ERROR_LOG_NAME)
+ const errorLogContent = fs.readFileSync(errorLogPath, 'utf8')
+ this.log(errorLogContent.split('\n').slice(-lines).join('\n'))
+ }
+}
diff --git a/packages/server/commands/log/out.ts b/packages/server/commands/log/out.ts
new file mode 100644
index 00000000..37c303b1
--- /dev/null
+++ b/packages/server/commands/log/out.ts
@@ -0,0 +1,26 @@
+// oclif command to get the latest 50 lines of error logs from the log file
+import { Command, Flags } from '@oclif/core'
+import path from 'node:path'
+import fs from 'node:fs'
+import { CODE_CLIMBER_META_DIR, LOG_NAME } from '../../utils/node.util'
+
+export default class LogOut extends Command {
+ static description = 'Get the latest 50 lines of error logs'
+
+ static flags = {
+ lines: Flags.string({
+ char: 'l',
+ description: 'Number of lines to get',
+ required: false,
+ }),
+ }
+
+ async run() {
+ const { flags } = await this.parse(LogOut)
+ const lines = flags.lines || 50
+ this.log(`Getting latest ${lines} lines of logs...`)
+ const logPath = path.join(CODE_CLIMBER_META_DIR, LOG_NAME)
+ const logContent = fs.readFileSync(logPath, 'utf8')
+ this.log(logContent.split('\n').slice(-lines).join('\n'))
+ }
+}
diff --git a/packages/server/package.json b/packages/server/package.json
index 43f997e6..7909bc85 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -44,8 +44,6 @@
"@nestjs/schedule": "^4.1.1",
"@nestjs/serve-static": "^4.0.2",
"@oclif/core": "^4.0.17",
- "@sentry/nestjs": "^8.25.0",
- "@sentry/profiling-node": "^8.25.0",
"axios": "^1.7.7",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
diff --git a/packages/server/src/app.module.ts b/packages/server/src/app.module.ts
index 57f094be..118d907e 100644
--- a/packages/server/src/app.module.ts
+++ b/packages/server/src/app.module.ts
@@ -5,13 +5,11 @@ import { AllExceptionsFilter } from './filters/allExceptions.filter'
import { RequestLoggerMiddleware } from './common/infrastructure/http/middleware/requestlogger.middleware'
import { ServeStaticModule } from '@nestjs/serve-static'
import { DbModule } from './v1/database/knex'
-import { SentryModule } from '@sentry/nestjs/setup'
import { APP_DIST_PATH } from '../utils/node.util'
import { ScheduledTaskModule } from './common/scheduler.module'
@Module({
imports: [
- SentryModule.forRoot(),
DbModule,
V1Module,
ScheduledTaskModule,
diff --git a/packages/server/src/main.ts b/packages/server/src/main.ts
index 3942f966..2607670b 100644
--- a/packages/server/src/main.ts
+++ b/packages/server/src/main.ts
@@ -1,5 +1,3 @@
-// Import this first!
-import './sentry'
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { Logger, ValidationPipe } from '@nestjs/common'
@@ -40,7 +38,7 @@ export const bootstrap = async () => {
]
: [
'https://codeclimbers.io',
- 'chrome-extension://fdmoefklpgbjapealpjfailnmalbgpbe',
+ /chrome-extension.+$/,
'http://localhost:5173',
/\.codeclimbers\.io$/,
/\.web\.app$/,
diff --git a/packages/server/src/sentry.ts b/packages/server/src/sentry.ts
deleted file mode 100644
index c5e52e45..00000000
--- a/packages/server/src/sentry.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as Sentry from '@sentry/nestjs'
-import { nodeProfilingIntegration } from '@sentry/profiling-node'
-import { isProd } from '../utils/environment.util'
-
-if (isProd()) {
- Sentry.init({
- dsn: 'https://e885e4c5eeed09d6229c7d7bfdc8d762@o4507772937043968.ingest.us.sentry.io/4507772946022400',
- integrations: [nodeProfilingIntegration()],
- // Performance Monitoring
- tracesSampleRate: 0.1, // Capture 100% of the transactions
-
- // Set sampling rate for profiling - this is relative to tracesSampleRate
- profilesSampleRate: 0.1,
- })
-}
diff --git a/packages/server/utils/node.util.ts b/packages/server/utils/node.util.ts
index d71c6265..ba7f3ea9 100644
--- a/packages/server/utils/node.util.ts
+++ b/packages/server/utils/node.util.ts
@@ -20,6 +20,8 @@ interface INodeUtil {
PROJECT_ROOT: string
BIN_PATH: string
START_ERR_LOG_MESSAGE: string
+ LOG_NAME: string
+ ERROR_LOG_NAME: string
HOME_DIR: string
CODE_CLIMBER_META_DIR: string
DB_PATH: string
@@ -35,8 +37,15 @@ abstract class BaseNodeUtil implements INodeUtil {
: path.join(__dirname, '..', '..', '..')
BIN_PATH = path.join(this.PROJECT_ROOT, 'bin')
HOME_DIR = os.homedir()
-
- abstract START_ERR_LOG_MESSAGE: string
+ LOG_NAME = 'codeclimbers.log'
+ ERROR_LOG_NAME = 'codeclimbers_error.log'
+ START_ERR_LOG_MESSAGE = pc.red(`
+ It seems the server is having trouble starting. Run the command
+
+ npx codeclimbers log:error -l 50
+
+ to investigate the issue further. You can also refer to https://github.com/CodeClimbersIO/cli/blob/release/docs/Troubleshooting.md or message us on our Discord
+ `)
abstract CODE_CLIMBER_META_DIR: string
abstract DB_PATH: string
APP_DIST_PATH = path.join(this.PROJECT_ROOT, 'packages', 'app', 'dist')
@@ -61,13 +70,6 @@ class DarwinNodeUtil extends BaseNodeUtil {
this.CODE_CLIMBER_META_DIR,
isTest() ? 'codeclimber.test.sqlite' : dbName,
)
- START_ERR_LOG_MESSAGE = pc.red(`
- It seems the server is having trouble starting. Run the command
-
- ${pc.white('cat ' + this.CODE_CLIMBER_META_DIR + '/codeclimbers_error.log')}
-
- to investigate the issue further. You can also refer to https://github.com/CodeClimbersIO/cli/blob/release/docs/Troubleshooting.md or message us on our Discord
- `)
NODE_PATH = (): string => {
const result = execSync('which node').toString().trim()
@@ -85,13 +87,8 @@ class WindowsNodeUtil extends BaseNodeUtil {
this.CODE_CLIMBER_META_DIR,
isTest() ? 'codeclimber.test.sqlite' : 'codeclimber.sqlite',
)
- START_ERR_LOG_MESSAGE: string = pc.red(`
- It seems the server is having trouble starting. Run the command in cmd (not powershell)
-
- ${pc.white('more ' + this.CODE_CLIMBER_META_DIR + '\\codeclimbers.err.log')}
-
- to investigate the issue further. You can also refer to https://github.com/CodeClimbersIO/cli/blob/release/docs/Troubleshooting.md or message us on our Discord
- `)
+ LOG_NAME = 'codeclimbers.out.log'
+ ERROR_LOG_NAME = 'codeclimbers.err.log'
NODE_PATH = (): string => {
const result = execSync('where node').toString().trim()
@@ -109,13 +106,7 @@ class LinuxNodeUtil extends BaseNodeUtil {
this.CODE_CLIMBER_META_DIR,
isTest() ? 'codeclimber.test.sqlite' : 'codeclimber.sqlite',
)
- START_ERR_LOG_MESSAGE = pc.red(`
- It seems the server is having trouble starting. Run the command
-
- ${pc.white('cat ' + this.CODE_CLIMBER_META_DIR + '/codeclimbers_error.log')}
-
- to investigate the issue further
- `)
+
NODE_PATH = (): string => {
const result = execSync('which node').toString().trim()
return path.dirname(result)
@@ -146,6 +137,8 @@ export const CODE_CLIMBER_META_DIR = nodeUtil.CODE_CLIMBER_META_DIR
export const DB_PATH = nodeUtil.DB_PATH
export const APP_DIST_PATH = nodeUtil.APP_DIST_PATH
export const CODE_CLIMBER_INI_PATH = nodeUtil.CODE_CLIMBER_INI_PATH
+export const LOG_NAME = nodeUtil.LOG_NAME
+export const ERROR_LOG_NAME = nodeUtil.ERROR_LOG_NAME
export const NODE_PATH = nodeUtil.NODE_PATH
export const initDBDir = nodeUtil.initDBDir
@@ -160,6 +153,8 @@ const logPaths = () => {
Logger.debug(HOME_DIR, 'HOME_DIR')
Logger.debug(APP_DIST_PATH, 'APP_DIST_PATH')
Logger.debug(CODE_CLIMBER_INI_PATH, 'CODE_CLIMBER_INI_PATH')
+ Logger.debug(LOG_NAME, 'LOG_NAME')
+ Logger.debug(ERROR_LOG_NAME, 'ERROR_LOG_NAME')
}
logPaths()