forked from clerk/javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeyless-node.ts
More file actions
173 lines (144 loc) · 5.34 KB
/
Copy pathkeyless-node.ts
File metadata and controls
173 lines (144 loc) · 5.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import type { AccountlessApplication } from '@clerk/backend';
import { logger } from '@clerk/shared/logger';
/**
* Attention: Only import this module when the node runtime is used.
* We are using conditional imports to mitigate bundling issues with Next.js server actions on version prior to 14.1.0.
*/
// @ts-ignore
import nodeRuntime from '#safe-node-apis';
import { createClerkClientWithOptions } from './createClerkClient';
/**
* The Clerk-specific directory name.
*/
const CLERK_HIDDEN = '.clerk';
/**
* The Clerk-specific lock file that is used to mitigate multiple key creation.
* This is automatically cleaned up.
*/
const CLERK_LOCK = 'clerk.lock';
/**
* The `.clerk/` is NOT safe to be commited as it may include sensitive information about a Clerk instance.
* It may include an instance's secret key and the secret token for claiming that instance.
*/
function updateGitignore() {
if (!nodeRuntime.fs) {
throw "Clerk: fsModule.fs is missing. This is an internal error. Please contact Clerk's support.";
}
const { existsSync, writeFileSync, readFileSync, appendFileSync } = nodeRuntime.fs;
if (!nodeRuntime.path) {
throw "Clerk: fsModule.path is missing. This is an internal error. Please contact Clerk's support.";
}
const gitignorePath = nodeRuntime.path.join(process.cwd(), '.gitignore');
if (!existsSync(gitignorePath)) {
writeFileSync(gitignorePath, '');
}
// Check if `.clerk/` entry exists in .gitignore
const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
if (!gitignoreContent.includes(CLERK_HIDDEN + '/')) {
appendFileSync(gitignorePath, `\n${CLERK_HIDDEN}/\n`);
}
}
const generatePath = (...slugs: string[]) => {
if (!nodeRuntime.path) {
throw "Clerk: fsModule.path is missing. This is an internal error. Please contact Clerk's support.";
}
return nodeRuntime.path.join(process.cwd(), CLERK_HIDDEN, ...slugs);
};
const _TEMP_DIR_NAME = '.tmp';
const getKeylessConfigurationPath = () => generatePath(_TEMP_DIR_NAME, 'keyless.json');
const getKeylessReadMePath = () => generatePath(_TEMP_DIR_NAME, 'README.md');
let isCreatingFile = false;
function safeParseClerkFile(): AccountlessApplication | undefined {
if (!nodeRuntime.fs) {
throw "Clerk: fsModule.fs is missing. This is an internal error. Please contact Clerk's support.";
}
const { readFileSync } = nodeRuntime.fs;
try {
const CONFIG_PATH = getKeylessConfigurationPath();
let fileAsString;
try {
fileAsString = readFileSync(CONFIG_PATH, { encoding: 'utf-8' }) || '{}';
} catch {
fileAsString = '{}';
}
return JSON.parse(fileAsString) as AccountlessApplication;
} catch {
return undefined;
}
}
const createMessage = (keys: AccountlessApplication) => {
return `\n\x1b[35m\n[Clerk]:\x1b[0m You are running on keyless mode.\nYou can \x1b[35mclaim your keys\x1b[0m by visiting ${keys.claimUrl}\n`;
};
async function createOrReadKeyless(): Promise<AccountlessApplication | undefined> {
if (!nodeRuntime.fs) {
// This should never happen.
throw "Clerk: fsModule.fs is missing. This is an internal error. Please contact Clerk's support.";
}
const { existsSync, writeFileSync, mkdirSync, rmSync } = nodeRuntime.fs;
/**
* If another request is already in the process of acquiring keys return early.
* Using both an in-memory and file system lock seems to be the most effective solution.
*/
if (isCreatingFile || existsSync(CLERK_LOCK)) {
return undefined;
}
isCreatingFile = true;
writeFileSync(
CLERK_LOCK,
// In the rare case, the file persists give the developer enough context.
'This file can be deleted. Please delete this file and refresh your application',
{
encoding: 'utf8',
mode: '0777',
flag: 'w',
},
);
const CONFIG_PATH = getKeylessConfigurationPath();
const README_PATH = getKeylessReadMePath();
mkdirSync(generatePath(_TEMP_DIR_NAME), { recursive: true });
updateGitignore();
/**
* When the configuration file exists, always read the keys from the file
*/
const envVarsMap = safeParseClerkFile();
if (envVarsMap?.publishableKey && envVarsMap?.secretKey) {
isCreatingFile = false;
rmSync(CLERK_LOCK, { force: true, recursive: true });
/**
* Notify developers.
*/
logger.logOnce(createMessage(envVarsMap));
return envVarsMap;
}
/**
* At this step, it is safe to create new keys and store them.
*/
const client = createClerkClientWithOptions({});
const accountlessApplication = await client.__experimental_accountlessApplications.createAccountlessApplication();
/**
* Notify developers.
*/
logger.logOnce(createMessage(accountlessApplication));
writeFileSync(CONFIG_PATH, JSON.stringify(accountlessApplication), {
encoding: 'utf8',
mode: '0777',
flag: 'w',
});
// TODO-KEYLESS: Add link to official documentation.
const README_NOTIFICATION = `
## DO NOT COMMIT
This directory is auto-generated from \`@clerk/nextjs\` because you are running on Keyless mode. Avoid committing the \`.clerk/\` directory as it includes the secret key of the unclaimed instance.
`;
writeFileSync(README_PATH, README_NOTIFICATION, {
encoding: 'utf8',
mode: '0777',
flag: 'w',
});
/**
* Clean up locks.
*/
rmSync(CLERK_LOCK, { force: true, recursive: true });
isCreatingFile = false;
return accountlessApplication;
}
export { createOrReadKeyless };